Hi

I did another test

I use a pi estimation algorithm and it is little bit more realistic than
just almost empty cycle body - still probably nobody will calculate pi in
plpgsql.

CREATE OR REPLACE FUNCTION pi_est(n int)
RETURNS numeric AS $$
DECLARE
  accum double precision DEFAULT 1.0;
  c1 double precision DEFAULT 2.0;
  c2 double precision DEFAULT 1.0;
  v constant double precision DEFAULT 2.0;
BEGIN
  FOR i IN 1..n
  LOOP
    accum := accum * ((c1 * c1) / (c2 * (c2 + v)));
    c1 := c1 + v;
    c2 := c2 + v;
  END LOOP;
  RETURN accum * v;
END;
$$ LANGUAGE plpgsql;

For this code the patch increased speed for 10000000 iterations from 6.3
sec to 4.7  .. it is speedup about 25%

The best performance (28%) is with code

CREATE OR REPLACE FUNCTION pi_est_2(n int)
RETURNS numeric AS $$
DECLARE
  accum double precision DEFAULT 1.0;
  c1 double precision DEFAULT 2.0;
  c2 double precision DEFAULT 1.0;
BEGIN
  FOR i IN 1..n
  LOOP
    accum := accum * ((c1 * c1) / (c2 * (c2 + double precision '2.0')));
    c1 := c1 + double precision '2.0';
    c2 := c2 + double precision '2.0';
  END LOOP;
  RETURN accum * double precision '2.0';
END;
$$ LANGUAGE plpgsql;

Unfortunately for unoptimized code the performance is worse (it is about
55% slower)

CREATE OR REPLACE FUNCTION pi_est_1(n int)
RETURNS numeric AS $$
DECLARE
  accum double precision DEFAULT 1.0;
  c1 double precision DEFAULT 2.0;
  c2 double precision DEFAULT 1.0;
BEGIN
  FOR i IN 1..n
  LOOP
    accum := accum * ((c1 * c1) / (c2 * (c2 + 2.0)));
    c1 := c1 + 2.0;
    c2 := c2 + 2.0;
  END LOOP;
  RETURN accum * 2.0;
END;
$$ LANGUAGE plpgsql;

same performance (bad) is for explicit casting

CREATE OR REPLACE FUNCTION pi_est_3(n int)
RETURNS numeric AS $$
DECLARE
  accum double precision DEFAULT 1.0;
  c1 double precision DEFAULT 2.0;
  c2 double precision DEFAULT 1.0;
BEGIN
  FOR i IN 1..n
  LOOP
    accum := accum * ((c1 * c1) / (c2 * (c2 + 2.0::double precision)));
    c1 := c1 + 2.0::double precision;
    c2 := c2 + 2.0::double precision;
  END LOOP;
  RETURN accum * double precision '2.0';
END;
$$ LANGUAGE plpgsql;

There is relative high overhead of cast from numeric init_var_from_num.

On master (without patching) the speed all double precision variants is
almost same.

This example can be reduced

CREATE OR REPLACE FUNCTION public.fx(integer)
 RETURNS double precision
 LANGUAGE plpgsql
AS $function$
DECLARE
  result double precision DEFAULT 1.0;
BEGIN
  FOR i IN 1..$1
  LOOP
    result := result * 1.000001::double precision;
  END LOOP;
  RETURN result;
END;
$function$

CREATE OR REPLACE FUNCTION public.fx_1(integer)
 RETURNS double precision
 LANGUAGE plpgsql
AS $function$
DECLARE
  result double precision DEFAULT 1.0;
BEGIN
  FOR i IN 1..$1
  LOOP
    result := result * 1.000001;
  END LOOP;
  RETURN result;
END;
$function$

CREATE OR REPLACE FUNCTION public.fx_2(integer)
 RETURNS double precision
 LANGUAGE plpgsql
AS $function$
DECLARE
  result double precision DEFAULT 1.0;
BEGIN
  FOR i IN 1..$1
  LOOP
    result := result * double precision '1.000001';
  END LOOP;
  RETURN result;
END;
$function$

Patched select fx(1000000) .. 400ms, fx_1 .. 400ms, fx_2  .. 126ms
Master fx(1000000) .. 180ms, fx_1 180 ms, fx_2 .. 180ms

So the patch has a problem with constant casting - unfortunately the mix of
double precision variables and numeric constants is pretty often in
Postgres.

Regards

Pavel

Attachment: test.sql
Description: application/sql

Reply via email to