Оптимизация функции

I00N
Дата: 02.04.2015 08:44:26
Добрый день.

Помогите, пожалуйста, максимально оптимизировать функцию по времени выполнения.

CREATE OR REPLACE FUNCTION next_id(OUT result bigint) AS $$
DECLARE
    our_epoch bigint := 1314220021721;
    seq_id bigint;
    now_millis bigint;
    shard_id int := 5;
BEGIN
    SELECT nextval('seq') % 1024 INTO seq_id;

    SELECT FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) INTO now_millis;
    result := (now_millis - our_epoch) << 23;
    result := result | (shard_id << 10);
    result := result | (seq_id);
END;
$$ LANGUAGE PLPGSQL;
Maxim Boguk
Дата: 02.04.2015 09:51:01
I00N
Добрый день.

Помогите, пожалуйста, максимально оптимизировать функцию по времени выполнения.

CREATE OR REPLACE FUNCTION next_id(OUT result bigint) AS $$
DECLARE
    our_epoch bigint := 1314220021721;
    seq_id bigint;
    now_millis bigint;
    shard_id int := 5;
BEGIN
    SELECT nextval('seq') % 1024 INTO seq_id;

    SELECT FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) INTO now_millis;
    result := (now_millis - our_epoch) << 23;
    result := result | (shard_id << 10);
    result := result | (seq_id);
END;
$$ LANGUAGE PLPGSQL;


а зачем сосбтвенно? Она не должна тормозить заметно так как ни к чему конкретно не обращается.
В принципе быстрее будет если ее в 1 строку написать вместо кучи переменных так как pl/pgsql интерпретатор и выполняет ваш код построчно. Но особо вы на этом не выйграете ничего (малые доли миллисекунд в лучшем случае).
Alexius
Дата: 02.04.2015 10:01:14
I00N,

а сколько времени она сейчас отрабатывает и сколько надо?

explain analyze select next_id() from generate_series(1,100000);
                                                         QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------
 Function Scan on generate_series  (cost=0.00..260.00 rows=1000 width=0) (actual time=35.795..4505.809 rows=100000 loops=1)
 Total runtime: 4523.334 ms
(2 rows)

Time: 4524.004 ms


у меня получается 0.038-0.045ms за вызов. с NOW() вместо clock_timestamp примерно так же.
I00N
Дата: 02.04.2015 16:29:20
Хочется выжать максимум из функции.

Сейчас:

"Function Scan on generate_series  (cost=0.00..260.00 rows=1000 width=0) (actual time=19.389..2303.164 rows=100000 loops=1)"
"Planning time: 0.026 ms"
"Execution time: 2329.626 ms"


После записи функции в одну строку стало гораздо быстрее:
CREATE OR REPLACE FUNCTION next_id(OUT result bigint) AS $$
DECLARE
    our_epoch constant bigint := 1314220021721;
    shard_id constant smallint := 5;
BEGIN
    result := ((FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)  - our_epoch)::bigint << 23) | (shard_id << 10) | (nextval('seq') % 1024);
END;
$$ LANGUAGE PLPGSQL;


"Function Scan on generate_series  (cost=0.00..260.00 rows=1000 width=0) (actual time=16.083..700.774 rows=100000 loops=1)"
"Planning time: 0.026 ms"
"Execution time: 716.802 ms"


Я мало знаком с PostgreSQL. Нормально ли написана данная функция? Есть ли что еще можно ускорить?

И еще вопрос.
Maxim Boguk
Дата: 02.04.2015 17:34:21
I00N
Хочется выжать максимум из функции.

Сейчас:

"Function Scan on generate_series  (cost=0.00..260.00 rows=1000 width=0) (actual time=19.389..2303.164 rows=100000 loops=1)"
"Planning time: 0.026 ms"
"Execution time: 2329.626 ms"


После записи функции в одну строку стало гораздо быстрее:
CREATE OR REPLACE FUNCTION next_id(OUT result bigint) AS $$
DECLARE
    our_epoch constant bigint := 1314220021721;
    shard_id constant smallint := 5;
BEGIN
    result := ((FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)  - our_epoch)::bigint << 23) | (shard_id << 10) | (nextval('seq') % 1024);
END;
$$ LANGUAGE PLPGSQL;


"Function Scan on generate_series  (cost=0.00..260.00 rows=1000 width=0) (actual time=16.083..700.774 rows=100000 loops=1)"
"Planning time: 0.026 ms"
"Execution time: 716.802 ms"


Я мало знаком с PostgreSQL. Нормально ли написана данная функция? Есть ли что еще можно ускорить?

И еще вопрос.


еще чуть быстрее - отказаться от переменных
our_epoch constant bigint := 1314220021721;
shard_id constant smallint := 5;
и вставить их в выражение явно.
Дальше уже начинает играть роль overhead на запуск самого pl/pgsql.
Потестируйте пустую функцию на скорость... вот быстрее чем это можно только на С написать.

--Maxim Boguk
www.postgresql-consulting.ru
vyegorov
Дата: 02.04.2015 17:37:44
I00N,

Попробуйте сделать PL/SQL функцию.
I00N
Дата: 02.04.2015 21:25:50
vyegorov
I00N,

Попробуйте сделать PL/SQL функцию.

Что Вы имеете ввиду учитывая, что PL/SQL - это процедурный язык в Oracle.
I00N
Дата: 02.04.2015 21:26:25
Maxim Boguk,

Благодарю.
vyegorov
Дата: 03.04.2015 01:00:28
I00N
vyegorov
I00N,
Попробуйте сделать PL/SQL функцию.

Что Вы имеете ввиду учитывая, что PL/SQL - это процедурный язык в Oracle.

Простите, некорректно сформулировал. Оракловский PL/SQL я не знаю и потому для меня эта аббревиатура ассоциируется только с Postgres'ом.

Если есть возможность выразить ваш преобразования на чистом SQL, то используйте функции на языке SQL.
I00N
Дата: 03.04.2015 15:42:46
vyegorov,

Благодарю, ускорилось в разы после переписывание на:

CREATE OR REPLACE FUNCTION next_id()returns bigint AS $$
    SELECT ((FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000)  - 1314220021721)::bigint << 23) | (5 << 10) | (nextval('seq') % 1024);
$$ LANGUAGE SQL;