6

STRICT当我在回答这个问题时声明了一个简单的 SQL 函数时,我偶然发现了性能下降。

为了演示,我创建了一个函数的两个变体,按升序对数组的两个元素进行排序。

测试设置

包含 10000 个随机整数对的表 (

CREATE TABLE tbl (arr int[]);

INSERT INTO tbl 
SELECT ARRAY[(random() * 1000)::int, (random() * 1000)::int]
FROM   generate_series(1,10000);

STRICT没有修饰符的函数:

CREATE OR REPLACE FUNCTION f_sort_array(int[])
  RETURNS int[]
  LANGUAGE sql IMMUTABLE AS
$func$
SELECT CASE WHEN $1[1] > $1[2] THEN ARRAY[$1[2], $1[1]] ELSE $1 END;
$func$;

带修饰符的函数STRICT(其他相同):

CREATE OR REPLACE FUNCTION f_sort_array_strict(int[])
  RETURNS int[]
  LANGUAGE sql IMMUTABLE STRICT AS
$func$
SELECT CASE WHEN $1[1] > $1[2] THEN ARRAY[$1[2], $1[1]] ELSE $1 END;
$func$;

结果

我执行了大约 20 次,并从EXPLAIN ANALYZE.

SELECT f_sort_array(arr)        FROM tbl;  -- Total runtime:  43 ms
SELECT f_sort_array_strict(arr) FROM tbl;  -- Total runtime: 103 ms

这些是 Debian Squeeze 上 Postgres 9.0.5 的结果。8.4 上的类似结果。

在所有 NULL 值的测试中,两个函数执行相同:~37 ms。

我做了一些研究,发现了一个有趣的问题。在大多数情况下,声明 SQL 函数STRICT 会禁用函数内联。更多关于PostgreSQL Online Journalpgsql-performance 邮件列表Postgres Wiki的信息。

但我不太确定这怎么可能是解释。在这个简单的场景中,不内联函数会导致性能下降?没有索引,没有光盘读取,没有排序。也许通过内联函数简化了重复函数调用的开销?

重新测试

同样的测试,同样的硬件,Postgres 9.1。更大的差异:

SELECT f_sort_array(arr)        FROM tbl;  -- Total runtime:  27 ms
SELECT f_sort_array_strict(arr) FROM tbl;  -- Total runtime: 107 ms

相同的测试,新硬件,Postgres 9.6。差距更大,然而:

SELECT f_sort_array(arr)        FROM tbl;  -- Total runtime:  10 ms
SELECT f_sort_array_strict(arr) FROM tbl;  -- Total runtime:  60 ms
4

2 回答 2

4

也许通过内联函数简化了重复函数调用的开销?

这就是我的猜测。你有一个非常简单的表达式。实际的函数调用可能涉及堆栈设置、传递参数等。

下面的测试给出了 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;
于 2011-12-10T09:12:42.587 回答
0

它是关于功能内联的,就像 Richard 的测试所怀疑和证实的那样。

为了清楚起见,Postgres Wiki列出了内联标量函数的要求(如我的示例):

  • 如果函数被声明STRICT,那么规划器必须能够证明NULL如果任何参数为空,主体表达式必然返回。目前,仅在以下情况下才满足此条件:每个参数至少被引用一次,并且正文中使用的所有函数、运算符和其他构造都是它们自己的STRICT

示例函数显然不符合条件。根据我的测试,构造函数和构造函数都应受到指责CASEARRAY

表函数(返回一组行)更加挑剔,但是:

  • 该函数未声明STRICT

如果函数不能内联,重复执行会重复收集函数开销。在后来的 Postgres 版本中,性能差异变得更大。

在当前笔记本电脑上使用PostgreSQL 13重新测试。更大的区别,然而:

SELECT f_sort_array(arr)        FROM tbl;  -- Total runtime:   4 ms
SELECT f_sort_array_strict(arr) FROM tbl;  -- Total runtime:  32 ms

在 dbfiddle.com, PostgreSQL 13上进行相同的测试。更大的区别,然而:

SELECT f_sort_array(arr)        FROM tbl;  -- Total runtime:   4 ms
SELECT f_sort_tblay_strict(arr) FROM tbl;  -- Total runtime: 137 ms (!)

综合测试,包括一半和全部 NULL 值的测试:

db<>在这里摆弄

于 2021-01-11T19:00:36.067 回答