Estou tentando construir uma função armazenada em C, que pegará um array de bigint como argumento e retornará outro array que é uma cópia do array de parâmetros, mas com todos os elementos incrementados em 1. Meu problema é que quando executo esse código como um programa independente , ele é executado muito rápido: 0,1s para elementos de 10M, mas leva 6s quando executado como procedimento armazenado Postgresql C.
Gostaria de saber se alguém pode identificar se estou fazendo algo obviamente errado?
Meu procedimento armazenado C:
#include "postgres.h"
#include "fmgr.h"
#include "utils/builtins.h"
#include "utils/array.h"
#include "catalog/pg_type.h"
PG_MODULE_MAGIC;
#define ARRPTR(x) ( (int64 *) ARR_DATA_PTR(x) )
#define ARRNELEMS(x) ArrayGetNItems(ARR_NDIM(x), ARR_DIMS(x))
PG_FUNCTION_INFO_V1(test_inc);
Datum
test_inc(PG_FUNCTION_ARGS)
{
ArrayType *a = PG_GETARG_ARRAYTYPE_P(0);
int n = ARRNELEMS(a);
int nbytes = ARR_OVERHEAD_NONULLS(1) + sizeof(int64) * n;
ArrayType *r = (ArrayType *) palloc0(nbytes);
SET_VARSIZE(r, nbytes);
ARR_NDIM(r) = 1;
r->dataoffset = 0; // marker for no null bitmap
ARR_ELEMTYPE(r) = INT8OID;
ARR_DIMS(r)[0] = n;
ARR_LBOUND(r)[0] = 1;
int64 *ad = ARRPTR(a);
int64 *rd = ARRPTR(r);
ereport(WARNING,
errcode(ERRCODE_WARNING),
errmsg("Before loop"));
for (int i = 0; i < n; i++) {
rd[i] = ad[i] + 1;
}
ereport(WARNING,
errcode(ERRCODE_WARNING),
errmsg("After loop"));
PG_RETURN_POINTER(r);
}
Eu compilo e instalo desta forma:
gcc -fPIC -O2 -I/usr/include/postgresql/15/server -I/usr/include/postgresql/internal -c test.c
gcc -shared -o test.so test.o
/usr/bin/install -c -m 755 test.so '/usr/lib/postgresql/15/lib/'
Código SQL que uso para testá-lo:
CREATE FUNCTION test_inc(_int8) RETURNS _int8 AS '/usr/lib/postgresql/15/lib/test.so'
LANGUAGE C IMMUTABLE PARALLEL SAFE;
create table t as select generate_series(0, 10000000) n;
create table t2 as select array_agg(n order by n) n from t;
create table t3 as select test_inc(n) n from t2;
A última consulta é executada em 6s. A remoção rd[i] = ad[i] + 1;
da linha faz com que o código seja executado em 0,6s. Além disso, olhando as mensagens de aviso, vejo que a execução travou em algum lugar após o loop, e não dentro do loop.
Meu código C autônomo se parece com o seguinte e é executado em 0,1s:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char ** argv) {
long n = 10000000;
long *a = (long*)malloc(n * sizeof(long));
long *r = (long*)malloc(n * sizeof(long));
for (int i = 0; i < n; i++) {
a[i] = i;
}
for (int i = 0; i < n; i++) {
r[i] = a[i] + 1;
}
// To make sure compiler does not remove previous loop because result is unused.
long res = 0;
for (int i = 0; i < n; i++) {
res += r[i];
}
printf("%ld", res);
}