Zum Inhalt springen

Benchmark-Suite

DuckDB hat eine umfangreiche Benchmark-Suite. Wenn Sie Änderungen vornehmen, die die Leistung beeinflussen können, sollten Sie diese Benchmarks ausführen, um mögliche Leistungsregressionen zu erkennen.

Erste Schritte

Um die Benchmark-Suite zu bauen, führen Sie im DuckDB-Repository den folgenden Befehl aus:

Terminal window
BUILD_BENCHMARK=1 BUILD_EXTENSIONS='tpch' make

Benchmarks auflisten

Um alle verfügbaren Benchmarks aufzulisten, führen Sie aus:

Terminal window
build/release/benchmark/benchmark_runner --list

Benchmarks ausführen

Einen einzelnen Benchmark ausführen

Um einen einzelnen Benchmark auszuführen, geben Sie den folgenden Befehl ein:

Terminal window
build/release/benchmark/benchmark_runner benchmark/micro/nulls/no_nulls_addition.benchmark

Die Ausgabe wird im CSV-Format auf stdout geschrieben, in folgendem Format:

name run timing
benchmark/micro/nulls/no_nulls_addition.benchmark 1 0.121234
benchmark/micro/nulls/no_nulls_addition.benchmark 2 0.121702
benchmark/micro/nulls/no_nulls_addition.benchmark 3 0.122948
benchmark/micro/nulls/no_nulls_addition.benchmark 4 0.122534
benchmark/micro/nulls/no_nulls_addition.benchmark 5 0.124102

Mit dem Flag --out können Sie auch eine Ausgabedatei angeben. Dann werden nur die Zeiten (durch Zeilenumbrüche getrennt) in diese Datei geschrieben.

Terminal window
build/release/benchmark/benchmark_runner benchmark/micro/nulls/no_nulls_addition.benchmark --out=timings.out

Die Ausgabe enthält Folgendes:

0.182472
0.185027
0.184163
0.185281
0.182948

Mehrere Benchmarks mit einem regulären Ausdruck ausführen

Sie können auch einen regulären Ausdruck angeben, um festzulegen, welche Benchmarks ausgeführt werden. Achten Sie auf die Shell-Expansion bestimmter Regex-Zeichen (z. B. wird * wahrscheinlich von Ihrer Shell expandiert; das erfordert korrektes Quoting oder Escaping).

Terminal window
build/release/benchmark/benchmark_runner "benchmark/micro/nulls/.*"

Alle Benchmarks ausführen

Ohne Argument werden alle Benchmarks ausgeführt.

Terminal window
build/release/benchmark/benchmark_runner

Weitere Optionen

Das Flag --info liefert weitere Informationen zum Benchmark.

Terminal window
build/release/benchmark/benchmark_runner benchmark/micro/nulls/no_nulls_addition.benchmark --info
display_name:NULL Addition (no nulls)
group:micro
subgroup:nulls

Das Flag --query gibt die Abfrage aus, die der Benchmark ausführt.

SELECT min(i + 1) FROM integers;

Das Flag --profile gibt einen Query-Tree aus.

Benchmarks erstellen

Ein Teil der Entwicklungsarbeit dreht sich um Leistung. Ein Benchmark neben den übrigen Tests bestätigt nicht nur Verbesserungen, sondern verhindert auch künftige Leistungsregressionen in der Funktion.

Benchmark-Beispiel

Zur Veranschaulichung, wie eine Benchmark-Datei aufgebaut ist, betrachten wir den Benchmark für die Window-Funktion FILL. (Die Funktion FILL interpoliert fehlende Werte in einer geordneten Partition linear.)

Benchmarks ähneln Unit-Test-Dateien und haben denselben Kopfzeilentyp.

benchmark/micro/window/window_fill.benchmark
# description: Measure the performance of FILL
# group: [window]

Der Befehl make format-head stellt sicher, dass der Kopf die erwartete Struktur hat und verhindert Fehler der Tidy-Prüfung.

Unter diesem Kopf stehen Schlüsselwörter, die den Benchmark zusammenfassen.

name FillPerformance
group micro
subgroup window

Manche Benchmarks führen eine einzelne Abfrage aus. Oft ist es nützlich, einen Benchmark mit dem Schlüsselwort argument zu parametrisieren. So lässt sich der Benchmark mit unterschiedlichen Einstellungen ausführen, etwa mit anderem Datenvolumen. Für den FILL-Benchmark gibt es drei Argumente:

argument sf 10
argument errors 0.1
argument keys 4

Für FILL sind das

  • Der Scale Factor (Millionen Zeilen pro Partition)
  • Die Fehlerrate (Anteil der fehlenden Werte)
  • Die Anzahl der Partitionen.

Benchmarks erfordern in der Regel eine Datenvorbereitung, bevor die Abfrage läuft. Die Datenvorbereitung steht im Abschnitt load der Benchmark-Datei. Für den FILL-Benchmark erzeugen wir eine Tabelle mit den Parametern und einem Zufallszahlengenerator.

load
select setseed(0.8675309);
create or replace table data as (
select
k::TINYINT as k,
(case when random() > ${errors} then m - 1704067200000 else null end) as v,
m,
from range(1704067200000, 1704067200000 + ${sf} * 1_000_000 * 10, 10) times(m)
cross join range(${keys}) keys(k)
);

Die Parameter von argument werden in der Abfrage expandiert, ähnlich wie foreach-Werte in Unit-Tests. Im Abschnitt load können mehrere SQL-Anweisungen stehen.

Sind die Daten vorbereitet, können wir die Abfrage festlegen, die wir benchmarken! Das geschieht im Abschnitt run. Die Einschränkungen sind dieselben wie bei einem Unit-Test (z. B. keine Leerzeilen usw.). Für den FILL-Benchmark wollen wir alle Stellen finden, an denen die Interpolation fehlschlägt:

run
SELECT
m,
k,
fill(v) OVER (PARTITION BY k ORDER BY m) as v
FROM
data
qualify v <> m - 1704067200000;

Wenn die Interpolation korrekt ist, gibt es unabhängig vom Scale keine Ausgabe. Das prüfen wir mit der abschließenden Klausel result, die dieselbe Syntax wie ein Unit-Test hat:

result III

Ohne Ausgabezeilen prüfen wir sowohl die Korrektheit der Abfrage als auch ihre Leistung.

Es gibt viele weitere Beispiele im Verzeichnis benchmark/ auf der obersten Ebene, und ein Blick dorthin lohnt sich, um weitere Techniken zu entdecken.