Author Topic: Image.statistics performance?  (Read 3950 times)

Offline mschuster

  • PTeam Member
  • PixInsight Jedi
  • *****
  • Posts: 1087
Image.statistics performance?
« on: 2014 December 14 18:53:50 »
I need to calculate statistics of many fixed sized arrays. I tried creating an equal length image, set its samples, and then use image.statistics methods. This works but it is maybe 100x slower than expected.

This script shows the problem. Data set is 4k arrays each of length 64. Putting all of the data in a single 64 x 4k image and then calculating statistics on the entire data set is fast. I don't actually need statistics across the entire data set, I am doing this just to provide a performance baseline.

But when I iterate through the data set, basically row by row, copying the length 64 rows into a small image, statistics is much slower, including the slice and setSamples overhead.

Is this a performance bug? Is there a faster way to do this?

Mike

PS: Of course I can write my own PJSR statistics methods. I did. They work faster. I was hoping that compiled PI code would be faster than PJSR, but it slower by a large factor, when using this small image, slice, setSamples technique.

Code: [Select]
mean: 131071.5, 0.005 s

standard deviation: 75674.59882065227, 0.015 s

Sn: 65537, 0.040 s

totalMean: 536868864, 4.331 s

totalStandardDeviation: 76263.36962570688, 8.205 s

totalSn: 69632, 3.900 s

Code: [Select]
var width = 64;
var height = 4 * 1024;

var data = new Array();
for (var i = 0; i != width * height; ++i) {
   data.push(i);
}

var image = new Image(width, height);
image.setSamples(data);

var start = new Date();
var mean = image.mean();
var stop = new Date();
console.writeln();
console.writeln(
   "mean: ", mean,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

var start = new Date();
var standardDeviation = image.stdDev();
var stop = new Date();
console.writeln();
console.writeln(
   "standard deviation: ", standardDeviation,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

var start = new Date();
var Sn = image.Sn();
var stop = new Date();
console.writeln();
console.writeln(
   "Sn: ", Sn,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

var image = new Image(width, 1);
var totalMean = 0;
var start = new Date();
for (var i = 0; i != height; ++i) {
   image.setSamples(data.slice(i * width, (i + 1) * width));
   totalMean += image.mean();
}
var stop = new Date();
console.writeln();
console.writeln(
   "totalMean: ", totalMean,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

var image = new Image(width, 1);
var totalStandardDeviation = 0;
var start = new Date();
for (var i = 0; i != height; ++i) {
   image.setSamples(data.slice(i * width, (i + 1) * width));
   totalStandardDeviation += image.stdDev();
}
var stop = new Date();
console.writeln();
console.writeln(
   "totalStandardDeviation: ", totalStandardDeviation,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

var image = new Image(width, 1);
var totalSn = 0;
var start = new Date();
for (var i = 0; i != height; ++i) {
   image.setSamples(data.slice(i * width, (i + 1) * width));
   totalSn += image.Sn();
}
var stop = new Date();
console.writeln();
console.writeln(
   "totalSn: ", totalSn,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

Offline Juan Conejero

  • PTeam Member
  • PixInsight Jedi Grand Master
  • ********
  • Posts: 7111
    • http://pixinsight.com/
Re: Image.statistics performance?
« Reply #1 on: 2014 December 15 02:18:05 »
Hi Mike,

The overhead of Image.setSamples() is not negligible. On the other hand, Image methods have been optimized for parallel processing of blocks of reasonable dimensions. In this example you are experiencing even more overhead caused by thread initialization.

The correct way to implement this task is by avoiding Image completely. You can use Math methods. The Matrix and Vector core PJSR objects also provide fast statistical methods and efficient to/from Array copy and conversion operations.

This modification of your script shows the performance difference that can be expected in this case:

Code: [Select]
var width = 64;
var height = 4 * 1024;

var data = new Array();
for (var i = 0; i != width * height; ++i) {
   data.push(i);
}

var image = new Image(width, height);
image.setSamples(data);

var start = new Date();
var mean = image.mean();
var stop = new Date();
console.writeln();
console.writeln(
   "mean: ", mean,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

var start = new Date();
var standardDeviation = image.stdDev();
var stop = new Date();
console.writeln();
console.writeln(
   "standard deviation: ", standardDeviation,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

var start = new Date();
var Sn = image.Sn();
var stop = new Date();
console.writeln();
console.writeln(
   "Sn: ", Sn,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

// ----------------------------------------------------------------------------

console.writeln( "\n*** slow!" );

var image = new Image(width, 1);
var totalMean = 0;
var start = new Date();
for (var i = 0; i != height; ++i) {
   image.setSamples(data.slice(i * width, (i + 1) * width));
   totalMean += image.mean();
}
var stop = new Date();
console.writeln();
console.writeln(
   "totalMean: ", totalMean,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

var image = new Image(width, 1);
var totalStandardDeviation = 0;
var start = new Date();
for (var i = 0; i != height; ++i) {
   image.setSamples(data.slice(i * width, (i + 1) * width));
   totalStandardDeviation += image.stdDev();
}
var stop = new Date();
console.writeln();
console.writeln(
   "totalStandardDeviation: ", totalStandardDeviation,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

var image = new Image(width, 1);
var totalSn = 0;
var start = new Date();
for (var i = 0; i != height; ++i) {
   image.setSamples(data.slice(i * width, (i + 1) * width));
   totalSn += image.Sn();
}
var stop = new Date();
console.writeln();
console.writeln(
   "totalSn: ", totalSn,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

// ----------------------------------------------------------------------------

console.writeln( "\n*** fast!" );

var fastMean = 0;
var start = new Date();
for ( var i = 0; i != height; ++i )
   fastMean += Math.mean( data.slice( i*width, (i + 1)*width ) );
var stop = new Date();
console.writeln();
console.writeln(
   "fastMean: ", fastMean,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

var fastStandardDeviation = 0;
var start = new Date();
for ( var i = 0; i != height; ++i )
   fastStandardDeviation += Math.stdDev( data.slice( i*width, (i + 1)*width ) );
var stop = new Date();
console.writeln();
console.writeln(
   "fastStandardDeviation: ", fastStandardDeviation,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

// There is no Math.Sn() method, but we can use Vector.Sn().
var rowVector = new Vector( width );
var fastSn = 0;
var start = new Date();
for ( var i = 0; i != height; ++i )
{
   rowVector.assign( data, i*width, (i + 1)*width );
   fastSn += rowVector.Sn();
}
var stop = new Date();
console.writeln();
console.writeln(
   "fastSn: ", fastSn,
   format(", %.03f s", 0.001 * (stop.getTime() - start.getTime()))
);

In the next version of the core application I'll include new methods of Image to control parallel processing, such as the maximum number of processors allowed on a per-instance basis. PCL already has this feature so it is just a matter of adding a few simple methods.


Juan Conejero
PixInsight Development Team
http://pixinsight.com/