Re: performance of sql and plpgsql functions

From: Julius Tuskenis <julius(dot)tuskenis(at)gmail(dot)com>
To: Pavel Stehule <pavel(dot)stehule(at)gmail(dot)com>
Cc: "pgsql-performance(at)postgresql(dot)org" <pgsql-performance(at)postgresql(dot)org>
Subject: Re: performance of sql and plpgsql functions
Date: 2024-06-17 11:01:00
Message-ID: 6a9ba80f-f504-4811-bb7b-d533dac1ea9a@gmail.com
Views: Raw Message | Whole Thread | Download mbox | Resend email
Thread:
Lists: pgsql-performance

Thank you Pavel for your input.

You wrote:

> no, PLpgSQL functions are not inlined
Yes, I understand that. I was referring to SQL functions (not plpgsql).

Regards,

Julius Tuskenis

On 2024-06-17 12:44, Pavel Stehule wrote:
>
>
> po 17. 6. 2024 v 11:35 odesílatel Julius Tuskenis
> <julius(dot)tuskenis(at)gmail(dot)com> napsal:
>
> Dear Postgresql performance guru,
>
> For some reason on our client server a function written in SQL
> language executes *100 times slower* than the one written in
> plpgsql...
>
> After updating to "PostgreSQL 12.18, compiled by Visual C++ build
> 1914, 64-bit" (from pg9.5) our client reported a performance
> issue. Everything boils down to a query that uses our function
> *public.fnk_saskaitos_skola *to calculate a visitors debt. The
> function is written in 'sql' language.
>
> The function is simple enough, marked STABLE
>
> ```
>
> CREATE OR REPLACE FUNCTION public.fnk_saskaitos_skola(prm_saskaita
> integer)
>  RETURNS numeric
>  LANGUAGE sql
>  STABLE SECURITY DEFINER
> AS $function$
>   SELECT
>     COALESCE(sum(mok_nepadengta), 0)
>   FROM
>     public.b_pardavimai
>     JOIN public.b_mokejimai ON (mok_pardavimas = pard_id)
>   WHERE
>     (pard_tipas = ANY('{1, 2, 6, 7}'))
>     AND (mok_saskaita = $1)
> $function$
> ;
>
> ```
>
> The problem is when I use it, it takes like 50ms to execute (on
> our client server).
>
> EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
>     SELECT * FROM fnk_saskaitos_skola(7141968)
>
>
> "Function Scan on public.fnk_saskaitos_skola (cost=0.25..0.26
> rows=1 width=32) (actual time=59.824..59.825 rows=1 loops=1)"
> "  Output: fnk_saskaitos_skola"
> "  Function Call: fnk_saskaitos_skola(7141968)"
> "  Buffers: shared hit=20"
> "Planning Time: 0.044 ms"
> "Execution Time: 59.848 ms"
>
>
> *How ever, if I rewrite the same function using plpgsql the result
> is quite different:*
>
> ```
>
> CREATE OR REPLACE FUNCTION public.fnk_saskaitos_skola_jt(IN
> prm_saskaita integer)
> RETURNS numeric
> LANGUAGE 'plpgsql'
> STABLE SECURITY DEFINER
> PARALLEL UNSAFE
> COST 100
> AS $BODY$
> begin
>   return (
>       SELECT
>         COALESCE(sum(mok_nepadengta), 0)
>       FROM
>         public.b_pardavimai
>         JOIN public.b_mokejimai ON (mok_pardavimas = pard_id)
>       WHERE
>         (pard_tipas = ANY('{1, 2, 6, 7}'))
>         AND (mok_saskaita = $1)
>   );
> end
> $BODY$;
>
> ```
>
>
> EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
>     SELECT fnk_saskaitos_skola_jt(7141968)
>
>
> ```
>
> "Result  (cost=0.00..0.26 rows=1 width=32) (actual
> time=0.562..0.562 rows=1 loops=1)"
> "  Output: fnk_saskaitos_skola_jt(7141968)"
> "  Buffers: shared hit=20"
> "Planning Time: 0.022 ms"
> "Execution Time: 0.574 ms"
>
> ```
>
>
> If I *analyze the sql that is inside the function* I get results
> similar to the ones of using plpgsql function:
>
> EXPLAIN (ANALYZE, BUFFERS, VERBOSE)
>   SELECT
>     COALESCE(sum(mok_nepadengta), 0)
>   FROM
>     public.b_pardavimai
>     JOIN public.b_mokejimai ON (mok_pardavimas = pard_id)
>   WHERE
>     (pard_tipas = ANY('{1, 2, 6, 7}'))
>     AND (mok_saskaita = 7141968)
>
> ```
>
> "Aggregate  (cost=2773.78..2773.79 rows=1 width=32) (actual
> time=0.015..0.016 rows=1 loops=1)"
> "  Output: COALESCE(sum((b_mokejimai.mok_nepadengta)::numeric),
> '0'::numeric)"
> "  Buffers: shared hit=4"
> "  ->  Nested Loop  (cost=1.00..2771.96 rows=730 width=3) (actual
> time=0.013..0.013 rows=0 loops=1)"
> "        Output: b_mokejimai.mok_nepadengta"
> "        Inner Unique: true"
> "        Buffers: shared hit=4"
> "        ->  Index Scan using idx_saskaita on public.b_mokejimai 
> (cost=0.56..793.10 rows=746 width=7) (actual time=0.012..0.012
> rows=0 loops=1)"
> "              Output: b_mokejimai.mok_id,
> b_mokejimai.mok_moketojas, b_mokejimai.mok_pardavimas,
> b_mokejimai.mok_laikas, b_mokejimai.mok_suma,
> b_mokejimai.mok_budas, b_mokejimai.mok_terminas,
> b_mokejimai.mok_cekis, b_mokejimai.mok_saskaita,
> b_mokejimai.mok_suma_bazine, b_mokejimai.mok_nepadengta,
> b_mokejimai.mok_padengta, b_mokejimai.mok_laiko_diena"
> "              Index Cond: (b_mokejimai.mok_saskaita = 7141968)"
> "              Buffers: shared hit=4"
> "        ->  Index Scan using pk_b_pardavimai_id on
> public.b_pardavimai  (cost=0.44..2.65 rows=1 width=4) (never
> executed)"
> "              Output: b_pardavimai.pard_id,
> b_pardavimai.pard_preke, b_pardavimai.pard_kaina,
> b_pardavimai.pard_nuolaida, b_pardavimai.pard_kiekis,
> b_pardavimai.pard_kasos_nr, b_pardavimai.pard_laikas,
> b_pardavimai.pard_prekes_id, b_pardavimai.pard_pirkejo_id,
> b_pardavimai.pard_pardavejas, b_pardavimai.pard_spausdinta,
> b_pardavimai.pard_reikia_grazinti, b_pardavimai.pard_kam_naudoti,
> b_pardavimai.pard_susieta, b_pardavimai.pard_galima_anuliuoti,
> b_pardavimai.pard_tipas, b_pardavimai.pard_pvm,
> b_pardavimai.pard_apsilankymas, b_pardavimai.pard_fk,
> b_pardavimai.pard_kelintas, b_pardavimai.pard_precekis,
> b_pardavimai.pard_imone, b_pardavimai.pard_grazintas,
> b_pardavimai.pard_debeto_sutartis, b_pardavimai.pard_kaina_be_nld,
> b_pardavimai.pard_uzsakymas_pos, b_pardavimai.pard_pvm_suma,
> b_pardavimai.pard_uzsakymo_nr, b_pardavimai.pard_nuolaidos_id,
> b_pardavimai.pard_nuolaida_taikyti,
> b_pardavimai.pard_pirkeja_keisti_galima,
> b_pardavimai.pard_suma_keisti_galima"
> "              Index Cond: (b_pardavimai.pard_id =
> b_mokejimai.mok_pardavimas)"
> "              Filter: (b_pardavimai.pard_tipas = ANY
> ('{1,2,6,7}'::smallint[]))"
> "Planning Time: 0.550 ms"
> "Execution Time: 0.049 ms"
>
> ```
>
>
> As I understand, the planning in case of sql functions is done
> everytime the functions is executed. I don't mind if planning
> would take 0.550 ms as when using plain SQL. But why execution
> takes ~59ms??...  What is it spent for?
>
> Isn't PosgreSQL supposed to inline simple SQL functions that are
> stable or immutable?
>
> no, PLpgSQL functions are not inlined
>
> Regards
>
> Pavel
>
> Any advice on  where to look for the cause of this "anomaly" is
> highly appreciated?
>
>
> I've tried executing the same query on different server and
> different database - I could not reproduce the behavior. Using SQL
> function produces results faster.
>
> I'd be gratefull to receive some insights of how to investigate
> the behavior. I'm not keen on changing the language or the
> function not knowing why it is required or how it helps...
>
>
>
> Regards,
>
> Julius Tuskenis
>
>
>

In response to

Browse pgsql-performance by date

  From Date Subject
Next Message Philip Semanchuk 2024-06-17 12:59:07 Re: performance of sql and plpgsql functions
Previous Message Pavel Stehule 2024-06-17 09:44:32 Re: performance of sql and plpgsql functions