I wonder about a slump in performance when a simple SQL function is declared STRICT
. I stumbled upon this phenomenon while answering a question here.
To demonstrate the effect I create two variants of a simple SQL function that orders two elements of an array in ascending order.
Test setup
-- temporary table with 10000 random pairs of integer
CREATE TEMP TABLE arr (i int[]);
INSERT INTO arr
SELECT ARRAY[(random() * 1000)::int, (random() * 1000)::int]
FROM generate_series(1,10000);
Function with STRICT
modifier:
CREATE OR REPLACE FUNCTION f_sort_array1(int[]) RETURNS int[] AS
$$
SELECT CASE WHEN $1[1] > $1[2] THEN ARRAY[$1[2], $1[1]] ELSE $1 END;
$$ LANGUAGE sql STRICT IMMUTABLE;
Function without STRICT
modifier (otherwise identical):
CREATE OR REPLACE FUNCTION f_sort_array2(int[]) RETURNS int[] AS
$$
SELECT CASE WHEN $1[1] > $1[2] THEN ARRAY[$1[2], $1[1]] ELSE $1 END;
$$ LANGUAGE sql IMMUTABLE;
Results
I executed each around 20 times and took the best result from EXPLAIN ANALYZE
.
SELECT f_sort_array1(i) FROM arr; -- Total runtime: 103 ms
SELECT f_sort_array2(i) FROM arr; -- Total runtime: 43 ms (!!!)
These are the results from a v9.0.5 server on Debian Squeeze. Similar results on v8.4. Did not test on 9.1, have no cluster at my disposal right now. (Can someone supply additional results for v9.1?)
Edit: In a test with 10000 NULL values both functions perform the same in the same test environment: ~37 ms.
I did some research and found an interesting gotcha. Declaring an SQL function STRICT disables function-inlining in most cases. More about that in the PostgreSQL Online Journal or in the pgsql-performance mailing list or in the Postgres Wiki.
But I am not quite sure how this could be the explanation. How can not inlining the function cause the performance slump in this simple scenario? No index, no disc read, no sorting. Maybe an overhead from the repeated function call that is streamlined away by inlining the function? Can you explain it? Or am I missing something?
Retest with Postgres 9.1
The same test on the same hardware with PostgreSQL 9.1 an found even bigger differences:
SELECT f_sort_array1(i) FROM arr; -- Total runtime: 107 ms
SELECT f_sort_array2(i) FROM arr; -- Total runtime: 27 ms (!!!)
Retest with Postgres 9.6
The same test on different hardware with PostgreSQL 9.6. The gap is even bigger, yet:
SELECT f_sort_array1(i) FROM arr; -- Total runtime: 60 ms
SELECT f_sort_array2(i) FROM arr; -- Total runtime: 10 ms (!!!)
Maybe an overhead from the repeated function call that is streamlined away by inlining the function?
That's what I'd guess. You've got a very simple expression there. An actual function-call presumably involves stack setup, passing parameters etc.
The test below gives run-times of 5ms for inlined and 50ms for strict.
BEGIN;
CREATE SCHEMA f;
SET search_path = f;
CREATE FUNCTION f1(int) RETURNS int AS $$SELECT 1$$ LANGUAGE SQL;
CREATE FUNCTION f2(int) RETURNS int AS $$SELECT 1$$ LANGUAGE SQL STRICT;
\timing on
SELECT sum(f1(i)) FROM generate_series(1,10000) i;
SELECT sum(f2(i)) FROM generate_series(1,10000) i;
\timing off
ROLLBACK;
来源:https://stackoverflow.com/questions/8455177/function-executes-faster-without-strict-modifier