Writing PostGIS raster Map Algebra Callback Functions in PLV8

I've read from many that PL/V8 mathematic operations are generally faster than what you get with SQL functions and PL/pgsql functions. One area where I thought this speed would be really useful was for writing Map Algebra call-back functions. A PostGIS 2.1+ map algebra callback function signature looks like: `func_name(double precision[][][] value, integer[][] pos, text[] VARIADIC userargs)`

So for starters, I set out to rewrite my favorite call-back function ST_Range4ma as a PL/V8. I left out all the logic for handling pos and userargs, because frankly I never use those. In doing so, I discovered a couple of things. The handling of pos and userargs really weighs down the built-in implementation. PL/V8 is indeed faster even if I were to write out all the for loop logic that the built-in PostGIS plpgsql version has. For demonstration here are 2 implementations of range function.

#### One in PL/V8

``````CREATE FUNCTION plv8_range4ma(value float8[][][],
pos integer[][][], VARIADIC userargs text[] DEFAULT NULL::text[])
RETURNS double precision AS
\$\$
return ( Math.max.apply(null, value) - Math.min.apply(null, value) );
\$\$
LANGUAGE plv8 IMMUTABLE;
``````

#### One in SQL

```CREATE FUNCTION sql_range4ma(value float8[][][],
pos integer[][][], VARIADIC userargs text[] DEFAULT NULL::text[])
RETURNS double precision AS
\$\$
SELECT MAX(v) - MIN(v) FROM unnest(\$1) As v;
\$\$
LANGUAGE sql IMMUTABLE;
```

Now to compare the speeds on the first band of an 800x678 pixel image.

``````-- plv8 8,241 ms
SELECT ST_AsPNG(ST_MapAlgebra (
rast
, 1,
'plv8_range4ma(double precision[][][], integer[][], text[])'::regprocedure,
'8BUI', 'FIRST', NULL, 4, 4
) ) As plv8_rele
FROM pics;

-- sql 17,701 ms
SELECT
ST_AsPNG(ST_MapAlgebra (
rast
, 1,
'sql_range4ma(double precision[][][], integer[][], text[])'::regprocedure,
'8BUI', 'FIRST', NULL, 4, 4
) )
FROM pics;

-- built-in st_range4ma 51,922 ms
SELECT
ST_AsPNG(ST_MapAlgebra (
rast
, 1,
'st_range4ma(double precision[][][], integer[][], text[])'::regprocedure,
'8BUI', 'FIRST', NULL, 4, 4
) )
FROM pics;
``````

There is however one big gotcha with the PL/V8 handling of arrays. It seems if you give it an n-dimensional array (a matrix) as is the case with MapAlgebra callbacks that get passed a neighborhood of pixel values, it collapses it down to a 1-dimensional array though all the original elements still appear to be in the collapsed array. This bummed me a bit when I ran into it, but then I realized for many use-cases such as Max, Min, Range the collapsing effect simplifies the work and has no effect on the result. For cases where you do need to keep track of the dimensions, I suspect you can create an SQL wrapper that includes the dimensions of the pixel value array and then in the plv8 logic need to break the array up based on the dimension. That will be my next venture.