为了演示这种效果,我创建了一个简单SQL函数的两个变体,它按升序对数组的两个元素进行排序.
测试设置
-- 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);
STRICT修饰符的功能:
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;
没有STRICT修饰符的功能(否则相同):
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;
结果
我执行了大约20次,并从EXPLAIN ANALYZE中获得了最好的结果.
SELECT f_sort_array1(i) FROM arr; -- Total runtime: 103 ms SELECT f_sort_array2(i) FROM arr; -- Total runtime: 43 ms (!!!)
这些是Debian Squeeze上的v9.0.5服务器的结果.关于v8.4的类似结果.没有在9.1上测试,现在没有我可支配的集群. (有人可以为v9.1提供额外的结果吗?)
编辑:
在具有10000个NULL值的测试中,两个函数在相同的测试环境中执行相同的操作:~37 ms.
我做了一些研究,发现了一个有趣的问题.在大多数情况下,声明SQL函数STRICT会禁用函数内联.更多关于PostgreSQL Online Journal或pgsql-performance mailing list或Postgres Wiki的内容.
但我不太确定这是怎么解释的.如何在这个简单的场景中内联函数导致性能下降?没有索引,没有光盘读取,没有排序.也许是通过内联函数简化了重复函数调用的开销?你能解释一下吗?或者我错过了什么?
用Postgres重新测试9.1
使用PostgreSQL 9.1在相同硬件上进行的相同测试发现了更大的差异:
SELECT f_sort_array1(i) FROM arr; -- Total runtime: 107 ms SELECT f_sort_array2(i) FROM arr; -- Total runtime: 27 ms (!!!)
用Postgres重新测试9.6
使用PostgreSQL 9.6在不同硬件上进行相同的测试.差距更大,但是:
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?
这就是我猜的.你在那里有一个非常简单的表达.实际的函数调用可能涉及堆栈设置,传递参数等.
下面的测试给出了内联5ms的运行时间和严格的50ms的运行时间.
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;