6

この質問STRICTに答えているときに、単純な SQL 関数を宣言すると、パフォーマンスが低下することに遭遇しました。

デモンストレーションのために、配列の 2 つの要素を昇順で並べ替える関数の 2 つのバリアントを作成しました。

テスト設定

整数の 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 ミリ秒。

私はいくつかの調査を行い、興味深い落とし穴を見つけました。SQL 関数STRICT を宣言すると、ほとんどの場合、関数のインライン展開が無効になります。詳細については、PostgreSQL オンライン ジャーナルpgsql-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

関数をインライン化することによって合理化された、繰り返しの関数呼び出しによるオーバーヘッドでしょうか?

それが私が推測するものです。そこにはとてもシンプルな表現があります。実際の関数呼び出しには、おそらくスタックのセットアップ、パラメーターの受け渡しなどが含まれます。

以下のテストでは、インラインの場合は 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;
于 2011-12-10T09:12:42.587 に答える