Re: plan cache overhead on plpgsql expression - Mailing list pgsql-hackers
From | Pavel Stehule |
---|---|
Subject | Re: plan cache overhead on plpgsql expression |
Date | |
Msg-id | CAFj8pRBH2T=7VoTrY7hf5MNUWE45zZpdWWvv+hzEem=Wn3GcNg@mail.gmail.com Whole thread Raw |
In response to | Re: plan cache overhead on plpgsql expression (Pavel Stehule <pavel.stehule@gmail.com>) |
Responses |
Re: plan cache overhead on plpgsql expression
|
List | pgsql-hackers |
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;
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;
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;
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;
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$
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$
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$
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
pgsql-hackers by date: