当前位置 : 主页 > 网络安全 > 测试自动化 >

性能 – 没有STRICT修饰符,函数执行速度更快?

来源:互联网 收集:自由互联 发布时间:2021-06-22
当一个简单的SQL函数被声明为STRICT时,我想知道性能的下降.我在 answering a question here时偶然发现了这种现象. 为了演示这种效果,我创建了一个简单SQL函数的两个变体,它按升序对数组的两
当一个简单的SQL函数被声明为STRICT时,我想知道性能的下降.我在 answering a question here时偶然发现了这种现象.

为了演示这种效果,我创建了一个简单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;
网友评论