Erw*_*ter 6 postgresql performance strict sql-function
我想知道当声明一个简单的SQL函数时性能是否会下降STRICT
。我在这里回答问题时偶然发现了这种现象。
为了演示这种效果,我创建了一个简单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);
Run Code Online (Sandbox Code Playgroud)
带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;
Run Code Online (Sandbox Code Playgroud)
没有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;
Run Code Online (Sandbox Code Playgroud)
我执行了大约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 (!!!)
Run Code Online (Sandbox Code Playgroud)
这些是Debian Squeeze上的v9.0.5服务器的结果。v8.4上的结果相似。没有在9.1上进行测试,现在没有可用的集群。(有人可以提供v9.1的其他结果吗?)
编辑:在具有10000个NULL值的测试中,两个函数在相同的测试环境中执行相同的操作:〜37 ms。
我做了一些研究,发现了一个有趣的陷阱。在大多数情况下,声明SQL函数STRICT会禁用函数内联。有关更多信息,请参见PostgreSQL在线期刊或pgsql-performance邮件列表或Postgres Wiki中。
但是我不太确定这可能是解释。在这种简单的情况下,如何不能内联函数会导致性能下降?没有索引,没有光盘读取,没有排序。也许重复的函数调用的开销通过内联函数而精简了吗?你能解释一下吗?还是我错过了什么?
使用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 (!!!)
Run Code Online (Sandbox Code Playgroud)
使用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 (!!!)
Run Code Online (Sandbox Code Playgroud)
也许通过内联函数可以简化重复函数调用的开销?
这就是我的猜测。你有一个非常简单的表达。实际的函数调用可能涉及堆栈设置、传递参数等。
下面的测试给出了内联运行时间为 5 毫秒,严格运行时间为 50 毫秒。
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;
Run Code Online (Sandbox Code Playgroud)