Jak mogę wygenerować losowy bajt

18

Chciałbym móc generować losowe byteapola o dowolnej długości (<1 Gb) do wypełniania danych testowych.

Jak najlepiej to zrobić?

Jack Douglas
źródło

Odpowiedzi:

20

Ulepszając odpowiedź Jacka Douglasa, aby uniknąć potrzeby zapętlania PL / PgSQL i konkatenacji bajtów, możesz użyć:

CREATE OR REPLACE FUNCTION random_bytea(bytea_length integer)
RETURNS bytea AS $body$
    SELECT decode(string_agg(lpad(to_hex(width_bucket(random(), 0, 1, 256)-1),2,'0') ,''), 'hex')
    FROM generate_series(1, $1);
$body$
LANGUAGE 'sql'
VOLATILE
SET search_path = 'pg_catalog';

Jest to prosta SQLfunkcja, której wywołanie jest tańsze niż PL / PgSQL.

Różnica w wydajności spowodowana zmienioną metodą agregacji jest ogromna w przypadku większych byteawartości. Chociaż oryginalna funkcja jest nawet trzykrotnie szybsza dla rozmiarów <50 bajtów, ta funkcja skaluje się znacznie lepiej dla większych wartości.

Lub użyj funkcji rozszerzenia C :

Zaimplementowałem generator losowych bajtów jako prostą funkcję rozszerzenia C. Znajduje się w moim repozytorium scrapcode na GitHub . Zobacz README tam.

Nukuje wydajność powyższej wersji SQL:

regress=# \a
regress=# \o /dev/null
regress=# \timing on
regress=# select random_bytea(2000000);
Time: 895.972 ms
regress=# drop function random_bytea(integer);
regress=# create extension random_bytea;
regress=# select random_bytea(2000000);
Time: 24.126 ms
Craig Ringer
źródło
1
Cóż, wymyśliłem prawie takie samo rozwiązanie, ale testowałem tylko pod kątem niższych wartości. Rozwiązanie There @ Jack było wyraźnym zwycięzcą. +1 za to, że tu się nie zatrzymałeś :)
dezso
Dziękuję - to jest doskonałe i prowokuje do myślenia. Myślę, że FROM generate_series(0, $1);musi być FROM generate_series(1, $1);. Czy próbowałeś rekursji? Moje ograniczone testy sugerują, że skaluje się to lepiej:
Jack Douglas
2
Próbowałem symlinking /dev/urandomsię /var/lib/pgsql/datai czytając ją pg_read_file()do premii szalonych punktów, ale niestety pg_read_file()czyta textwejście poprzez konwersję kodowania, więc nie może czytać bytea. Jeśli naprawdę chcesz maksymalnej prędkości, napisz funkcję Crozszerzającą, która używa szybkiego generatora liczb pseudolosowych do tworzenia danych binarnych i owijaj dane bajtowe wokół bufora :-)
Craig Ringer
1
@JackDouglas Nie mogłem nic na to poradzić. Wersja C rozszerzenia random_bytea. github.com/ringerc/scrapcode/tree/master/postgresql/…
Craig Ringer
1
Kolejna doskonała odpowiedź! Właściwie jeden z najlepszych, jakie do tej pory widziałem. Nie testowałem rozszerzenia, ale ufam, że działa tak, jak w reklamie.
Erwin Brandstetter,
5

Chciałbym móc generować losowe pola bajtów o dowolnej długości

Ta funkcja to zrobi, ale 1 Gb zajmie dużo czasu, ponieważ nie skaluje się liniowo z długością wyjściową:

create function random_bytea(p_length in integer) returns bytea language plpgsql as $$
declare
  o bytea := '';
begin 
  for i in 1..p_length loop
    o := o||decode(lpad(to_hex(width_bucket(random(), 0, 1, 256)-1),2,'0'), 'hex');
  end loop;
  return o;
end;$$;

test wyjściowy:

select random_bytea(2);

/*
|random_bytea|
|:-----------|
|\xcf99      |
*/

select random_bytea(10);

/*
|random_bytea          |
|:---------------------|
|\x781b462c3158db229b3c|
*/

select length(random_bytea(100000))
     , clock_timestamp()-statement_timestamp() time_taken;

/*
|length|time_taken     |
|-----:|:--------------|
|100000|00:00:00.654008|
*/

dbfiddle tutaj

Jack Douglas
źródło
Ładne użycie width_bucket. Poręczny.
Craig Ringer
1
Udoskonaliłem twoje podejście, aby uniknąć PL / PgSQL i kosztownej pętli konkatenacji; zobacz nową odpowiedź. Używając string_agg zamiast generator_series zamiast pętli konkatenacji PL / PgSQL na bytea widzę 150-krotną poprawę wydajności.
Craig Ringer