Einführung
Die meisten Entwickler haben eindeutig von einigen ziemlich bedeutenden Open-Source-Entwicklungen wie dem LLVM-System und dem Clang-Compiler gehört. LLVM ist jetzt jedoch nicht nur das System selbst zum Erstellen von Compilern, sondern auch ein großes Ökosystem, das viele Projekte zur Lösung verschiedener Probleme umfasst, die in jeder Phase der Compilererstellung auftreten (normalerweise verfügt jedes dieser Projekte über ein eigenes Repository). Ein Teil der Infrastruktur umfasst natürlich Test- und Benchmarking-Tools wie Bei der Entwicklung eines Compilers ist seine Wirksamkeit ein sehr wichtiger Indikator. Eines dieser einzelnen LLVM-Testinfrastrukturprojekte ist die Testsuite (
offizielle Dokumentation ).
LLVM-Testsuite
Auf den ersten Blick scheint es, dass dies nur eine Reihe von Benchmarks in C / C ++ ist, aber dies ist nicht ganz richtig. Neben dem Quellcode der Programme, an denen Leistungsmessungen durchgeführt werden, enthält die Testsuite eine flexible Infrastruktur zum Erstellen, Ausführen und Sammeln von Metriken. Standardmäßig werden die folgenden Metriken erfasst: Kompilierungszeit, Ausführungszeit, Verknüpfungszeit, Codegröße (in Abschnitten).
Die Testsuite ist natürlich nützlich zum Testen und Benchmarking von Compilern, kann aber auch für einige andere Forschungsaufgaben verwendet werden, bei denen eine C / C ++ - Codebasis erforderlich ist. Diejenigen, die einmal versucht haben, etwas auf dem Gebiet der Datenanalyse zu tun, standen meines Erachtens vor dem Problem des Mangels und der Fragmentierung der Quelldaten. Eine Testsuite besteht zwar nicht aus einer Vielzahl von Anwendungen, verfügt jedoch über einen einheitlichen Datenerfassungsmechanismus. Das Hinzufügen eigener Anwendungen zur Sammlung und das Sammeln der für Ihre spezielle Aufgabe erforderlichen Metriken ist sehr einfach. Daher ist meiner Meinung nach die Testsuite (zusätzlich zu den Hauptaufgaben des Testens und Benchmarking) eine gute Option für ein Basisprojekt, auf dessen Grundlage Sie Ihre eigene Datenerfassung für Aufgaben erstellen können, bei denen Sie einige Funktionen des Programmcodes oder einige Merkmale der Programme analysieren müssen.
LLVM-Testsuite-Struktur
test-suite |----CMakeLists.txt // CMake , , | // .. | |---- cmake | |---- .modules // , | // API | |---- litsupport // Python, test-suite, | // lit ( LLVM) | |---- tools // : | // ( | // ), .. | | // | |---- SingleSource // , | // . . | |---- MultiSource // , | // . | // . | |---- MicroBenchmarks // , google-benchmark. | // , , | // | |---- External // , test-suite, | // , ( ) | // -
Die Struktur ist einfach und unkompliziert.
Arbeitsprinzip
Wie Sie sehen können, sind CMake und ein spezielles Lit-Test-Format für alle Arbeiten zur Beschreibung der Zusammenstellung, des Starts und der Erfassung von Metriken verantwortlich.
Wenn wir es sehr abstrakt betrachten, ist es klar, dass der Benchmarking-Prozess mit diesem System einfach und sehr vorhersehbar aussieht:

Wie sieht das genauer aus? In diesem Artikel möchte ich genau darauf eingehen, welche Rolle CMake im gesamten System spielt und welche einzige Datei Sie schreiben müssen, wenn Sie diesem System etwas hinzufügen möchten.
1. Erstellen von Testanwendungen.Als Build-System ist es zum De-facto-Standard für C / C ++ - CMake-Programme geworden. CMake konfiguriert das Projekt und generiert je nach Benutzereinstellung make-, ninja- usw. Dateien. für den direkten Bau.
In der Testsuite generiert CMake jedoch nicht nur Regeln zum Erstellen von Anwendungen, sondern konfiguriert auch die Tests selbst.
Nach dem Starten von CMake werden weitere Dateien (mit der Erweiterung .test) in das Build-Verzeichnis geschrieben, in denen beschrieben wird, wie die Anwendung ausgeführt und auf Richtigkeit überprüft werden soll.
Beispiel für die Standard-Testdatei
RUN: cd <some_path_to_build_directory>/MultiSource/Benchmarks/Prolangs-C/football ; <some_path_to_build_directory>/MultiSource/Benchmarks/Prolangs-C/football/football VERIFY: cd <some_path_to_build_directory>/MultiSource/Benchmarks/Prolangs-C/football ; <some_path_to_build_directory>/tools/fpcmp %o football.reference_output
Die Datei mit der Erweiterung .test kann die folgenden Abschnitte enthalten:
- PREPARE - beschreibt alle Aktionen, die vor dem Starten der Anwendung ausgeführt werden müssen, sehr ähnlich der Before-Methode, die in verschiedenen Unit-Test-Frameworks vorhanden ist.
- RUN - beschreibt, wie die Anwendung ausgeführt wird.
- VERIFY - beschreibt, wie der korrekte Betrieb der Anwendung überprüft wird.
- METRIC - beschreibt die Metriken, die im Standard zusätzlich erfasst werden müssen.
Jeder dieser Abschnitte kann weggelassen werden.
Da diese Datei jedoch automatisch generiert wird, wird in der CMake-Datei für den Benchmark beschrieben, wie die Objektdateien abgerufen, zu einer Anwendung zusammengesetzt und anschließend mit dieser Anwendung gearbeitet werden.
Betrachten Sie ein Beispiel für einige CMakeLists.txt, um das Standardverhalten und dessen Beschreibung besser zu verstehen
list(APPEND CFLAGS -DBREAK_HANDLER -DUNICODE-pthread)
Flags können je nach Plattform gesetzt werden. Die DetectArchitecture-Datei ist in den cmake-Modulen der Testsuite enthalten. Diese bestimmt die Zielplattform, auf der die Benchmarks ausgeführt werden, sodass Sie einfach die bereits gesammelten Daten verwenden können. Andere Daten sind ebenfalls verfügbar: Betriebssystem, Bytereihenfolge usw.
if(TARGET_OS STREQUAL "Linux") list(APPEND CPPFLAGS -DC_LINUX) endif() if(NOT ARCH STREQUAL "ARM") if(ENDIAN STREQUAL "little") list(APPEND CPPFLAGS -DFPU_WORDS_BIGENDIAN=0) endif() if(ENDIAN STREQUAL "big") list(APPEND CPPFLAGS -DFPU_WORDS_BIGENDIAN=1) endif() endif()
Grundsätzlich sollte dieser Teil nichts Neues für Personen sein, die mindestens einmal eine einfache CMake-Datei gesehen oder geschrieben haben. Natürlich können Sie die Bibliotheken verwenden, sie selbst erstellen und im Allgemeinen alle von CMake bereitgestellten Mittel verwenden, um den Prozess der Erstellung Ihrer Anwendung zu beschreiben.
Und dann müssen Sie die Generierung der Testdatei sicherstellen. Welche Tools bietet die tets-suite-Schnittstelle dafür?
Es gibt zwei grundlegende Makros
llvm_multisource und
llvm_singlesource , die für die meisten trivialen Fälle ausreichen.
- llvm_multisource wird verwendet, wenn die Anwendung aus mehreren Dateien besteht. Wenn Sie die Quellcodedateien beim Aufrufen dieses Makros in Ihrem CMake nicht als Parameter übergeben, werden alle Quellcodedateien im aktuellen Verzeichnis als Basis für die Erstellung verwendet. Tatsächlich finden derzeit Änderungen an der Schnittstelle dieses Makros in der Testsuite statt, und die beschriebene Methode zum Übertragen von Quelldateien als Makroparameter ist die aktuelle Version im Hauptzweig. Zuvor gab es ein anderes System: Dateien mit Quellcode mussten in die Quellvariable geschrieben werden (wie in Release 7.0), und das Makro akzeptierte keine Parameter. Die grundlegende Logik der Implementierung ist jedoch dieselbe geblieben.
- llvm_singlesource berücksichtigt, dass jede .c / .cpp-Datei ein separater Benchmark ist und für jede eine separate ausführbare Datei sammelt.
Standardmäßig generieren beide oben beschriebenen Makros zum Starten einer erstellten Anwendung einen Befehl, der diese Anwendung einfach aufruft. Die Korrektheitsprüfung erfolgt aufgrund des Vergleichs mit der erwarteten Ausgabe in der Datei mit der Erweiterung .reference_output (auch mit möglichen Suffixen .reference_output.little-endian, .reference_output.big-endian).
Wenn dies zu Ihnen passt, ist es einfach großartig. Eine zusätzliche Zeile (Aufruf von llvm_multisource oder llvm_singlesource) reicht aus, um die Anwendung zu starten und die folgenden Metriken abzurufen: Codegröße (in Abschnitten), Kompilierungszeit, Verbindungszeit, Ausführungszeit.
Aber das passiert natürlich selten so reibungslos. Möglicherweise müssen Sie eine oder mehrere Stufen ändern. Und das ist auch mit einfachen Aktionen möglich. Das einzige, woran Sie sich erinnern müssen, ist, dass Sie, wenn Sie eine Phase neu definieren, alle anderen beschreiben müssen (auch wenn der Standardalgorithmus ihrer Arbeit passt, was natürlich ein wenig ärgerlich ist).
Die API enthält Makros, mit denen Aktionen in jeder Phase beschrieben werden.
Über das Makro
llvm_test_prepare für die Vorbereitungsphase
gibt es nicht viel zu schreiben. Die Befehle, die Sie ausführen müssen, werden dort einfach als Parameter übergeben.
Was könnte im Startabschnitt benötigt werden? Der vorhersehbarste Fall ist, dass die Anwendung einige Argumente und Eingabedateien akzeptiert. Dazu gibt es das Makro
llvm_test_run , das nur die Anwendungsstartargumente (ohne den Namen der ausführbaren Datei) als Parameter akzeptiert.
llvm_test_run(--fixed 400 --cpu 1 --num 200000 --seed 1158818515 run.hmm)
Um die Aktionen in der Validierungsphase zu ändern, wird das Makro
llvm_test_verify verwendet , das alle Befehle als Parameter akzeptiert. Um die Richtigkeit zu überprüfen, ist es natürlich besser, die im Werkzeugordner enthaltenen Werkzeuge zu verwenden. Sie bieten gute Möglichkeiten zum Vergleichen der generierten Ausgabe mit der erwarteten (es gibt eine separate Verarbeitung zum Vergleichen von reellen Zahlen mit Fehlern usw.). Sie können aber irgendwo überprüfen, ob die Bewerbung erfolgreich abgeschlossen wurde usw.
llvm_test_verify("cat %o | grep -q 'exit 0'")
Was aber, wenn zusätzliche Messdaten erfasst werden müssen?
Hierfür gibt es ein
llvm_test_metric- Makro.
llvm_test_metric(METRIC < > <, >)
Beispielsweise kann für Dhrystone eine dafür spezifische Metrik erhalten werden.
llvm_test_metric(METRIC dhry_score grep 'Dhrystones per Second' %o | awk '{print $4}')
Wenn Sie zusätzliche Metriken für alle Tests erfassen müssen, ist diese Methode natürlich etwas unpraktisch. Entweder müssen Sie den Aufruf llvm_test_metric zu den übergeordneten Makros hinzufügen, die von der Schnittstelle bereitgestellt werden, oder Sie können TEST_SUITE_RUN_UNDER (die CMake-Variable) und ein bestimmtes Skript verwenden, um Metriken zu erfassen. Die Variable TEST_SUITE_RUN_UNDER ist sehr nützlich und kann beispielsweise zum Ausführen auf Simulatoren usw. verwendet werden. Tatsächlich wird ein Befehl geschrieben, der die Anwendung mit ihren Argumenten als Eingabe akzeptiert.
Als Ergebnis erhalten wir einige CMakeLists.txt des Formulars
Die Integration erfordert keinen zusätzlichen Aufwand. Wenn die Anwendung bereits mit CMake erstellt wurde, können Sie in CMakeList.txt in der Testsuite das vorhandene CMake für die Assembly einbinden und einige einfache Makroaufrufe hinzufügen.
2. Ausführen von TestsAls Ergebnis seiner Arbeit hat CMake eine spezielle Testdatei gemäß der angegebenen Beschreibung erstellt. Aber wie wird diese Datei ausgeführt?
lit verwendet immer eine Konfigurationsdatei lit.cfg, die dementsprechend in der Testsuite vorhanden ist. In dieser Konfigurationsdatei werden verschiedene Einstellungen zum Ausführen von Tests angegeben, einschließlich des Formats ausführbarer Tests. Die Test-Suite verwendet ein eigenes Format, das sich im Ordner litsupport befindet.
config.test_format = litsupport.test.TestSuiteTest()
Dieses Format wird als Testklasse beschrieben, die vom standardmäßigen beleuchteten Test geerbt wurde und die Hauptmethode der Ausführungsschnittstelle überschreibt. Wichtige Komponenten von litsupport ist auch eine Klasse mit einer Beschreibung des TestPlan-Testausführungsplans, in der alle Befehle gespeichert sind, die in verschiedenen Phasen ausgeführt werden müssen, und die Reihenfolge der Phasen kennen. Um die erforderliche Flexibilität zu gewährleisten, wurden auch Module in die Architektur eingeführt, die die mutatePlan-Methode bereitstellen sollen, in der sie den Testplan ändern können. Sie führen lediglich eine Beschreibung der Sammlung der erforderlichen Metriken ein und fügen zusätzliche Befehle zum Messen der Zeit zum Starten der Anwendung usw. hinzu. Aufgrund dieser Lösung wird die Architektur gut erweitert.

Ein Beispiel für die Testsuite der Testsuite (mit Ausnahme von Details in Form von TestContext-Klassen, verschiedenen beleuchteten Konfigurationen und den Tests selbst usw.) ist unten dargestellt.

Leuchtet, wird der in der Konfigurationsdatei angegebene Testtyp ausgeführt. TestSuiteTest analysiert die generierte CMake-Testdatei und erhält eine Beschreibung der Hauptphasen. Anschließend werden alle gefundenen Module aufgerufen, um den aktuellen Testplan zu ändern. Der Start wird instrumentiert. Anschließend wird der empfangene Testplan ausgeführt: Sie werden in der Reihenfolge der Vorbereitung, des Starts und der Validierung ausgeführt. Bei Bedarf kann eine Profilerstellung durchgeführt werden (wird von einem der Module hinzugefügt, wenn während der Konfiguration eine Variable festgelegt wurde, die auf die Notwendigkeit einer Profilerstellung hinweist). Der nächste Schritt besteht darin, Metriken zu erfassen, die Funktionen zum Sammeln, die von Standardmodulen im Feld metric_collectors in TestPlan hinzugefügt wurden, und anschließend zusätzliche vom Benutzer in CMake beschriebene Metriken zu erfassen.
3. Ausführen der TestsuiteEs gibt zwei Möglichkeiten, die Testsuite auszuführen:
Das Ergebnis für jeden Test wird als angezeigt
PASS: test-suite :: MultiSource/Benchmarks/Prolangs-C/football/football.test (m of n) ********** TEST 'test-suite :: MultiSource/Benchmarks/Prolangs-C/football/football.test' RESULTS ********** compile_time: 1.1120 exec_time: 0.0014 hash: "38254c7947642d1adb9d2f1200dbddf7" link_time: 0.0240 size: 59784 size..bss: 99800 … size..text: 37778 **********
Die Ergebnisse verschiedener Starts können ohne LNT verglichen werden (obwohl dieses Framework großartige Möglichkeiten zur Analyse von Informationen mit verschiedenen Tools bietet, aber eine separate Überprüfung erfordert), indem das in der Testsuite enthaltene Skript verwendet wird
test-suite/utils/compare.py results_a.json results_b.json
Ein Beispiel für den Vergleich der Codegröße ein und desselben Benchmarks aus zwei Starts: mit den Flags -O3 und -Os
test-suite/utils/compare.py -m size SANDBOX1/build/O3.json SANDBOX/build/Os.json Tests: 1 Metric: size Program O3 Os diff test-suite...langs-C/football/football.test 59784 47496 -20.6%
Fazit
Die Infrastruktur zum Beschreiben und Ausführen von in der Testsuite implementierten Benchmarks ist einfach zu verwenden und zu unterstützen, lässt sich gut skalieren und verwendet meiner Meinung nach im Prinzip ziemlich elegante Lösungen in ihrer Architektur, was die Testsuite natürlich zu einem sehr nützlichen Werkzeug für Entwickler macht Compiler sowie dieses System können für die Verwendung in einigen Datenanalyseaufgaben geändert werden.