W artykułach o sprytnym kodzie i mikrooptymalizacjach postawiłem tezę, iż często większa wydajność kodu, którą usprawiedliwiamy zmniejszenie czytelności, jest fikcją. Aby to stwierdzić potrzebujemy danych. Tymi danymi są czas wykonania aplikacji i kod asemblerowy generowany przez kompilator. Dzisiaj opiszę narzędzie przeglądarkowe pozwalające nam analizować właśnie kod asemblerowy. A jest nim Compiler Explorer.
Czym jest Compiler Explorer?
Compiler Explorer, znany także jako Godbolt ze względu na adres strony i nazwisko autora, to kompilator online. Możemy tam wpisać fragment kodu w C, czy C++ i zobaczyć jaki wygeneruje kod asemblerowy dla wybranego kompilatora i flag.
Wygląda to mniej więcej tak:
Kod asemblerowy odpowiedzialny za poszczególne fragmenty kodu wejściowego jest zaznaczony na odpowiednie kolory. Możemy sobie z listy wybierać inne kompilatory, czy wpisywać dodatkowe opcje kompilacji jak na przykład optymalizację. Dzięki temu jesteśmy w stanie gwałtownie sprawdzić jak zmienia się kod wynikowy.
Jeżeli nie jesteśmy biegli w znaczeniu instrukcji asemblerowych, możemy sobie na nie najechać myszką i przeczytać wyskakujący opis.
Lista wspieranych kompilatorów jest całkiem spora i zawiera choćby gcc, clang, czy msvc. Mamy też do wyboru całkiem sporo architektur procesorów. Są oczywiście x86 i ARM, ale również AVR, RISC-V, MIPS, a choćby Arduino i Raspbian.
Mamy do wyboru również inne języki niż C i C++. Tak, wśród nich jest Ada. Poza tym jest też na przykład Rust.
A komu to potrzebne?
Developerowi chcącemu sprawdzić wydajność kodu. Przypadków, kiedy sprawdzenie czegoś gwałtownie w Godbolcie może być całkiem sporo. Na przykład mamy w zespole dyskusję na temat sensu stosowania jakiejś konstrukcji w kodzie. Czy mniej czytelny kod faktycznie skutkuje zwiększeniem wydajności? Dopóki nie mamy dowodów nie jesteśmy w stanie odpowiedzieć na to pytanie.
Czy dana optymalizacja ma sens?
W artykule o sprytnym kodzie na przykład podałem kilka przykładów, które mogą wywołać podobną dyskusję. Weźmy na przykład funkcję my_string z tamtego artykułu. Okazuje się, iż na x86 64-bit i optymalizacji O2 obie wersje dają ten sam kod wynikowy:
Z kolei o ile zamienimy optymalizację na -O0, okazuje się, iż bardziej rozwlekła wersja w C daje choćby krótszy kod asemblerowy (możemy otworzyć sobie Diff view, żeby zobaczyć dokładnie różnice):
To jeszcze nie znaczy, iż mniej instrukcji się szybciej wykona (szczególnie w przypadku pętli). Aby mieć pewność należy wykonać benchmark. Jednak dla optymalizacji O2 wiemy na pewno, iż output jest taki sam. Są to twarde dane pozwalające nam podjąć decyzję projektową.
Przy okazji tego przykładu mogliśmy również sobie zobaczyć jak kod wynikowy różni się między poziomami optymalizacji O0 i O2.
Czy używam FPU?
Inny interesujący przykład pokazujący jak Godbolt może się przydać odnosi się do aktywacji operacji zmiennoprzecinkowych ze wsparciem hardware’u, o czym pisałem tutaj:
Mamy kompilator ARM GCC 8.2 i kompilujemy na Cortex-M4. W pierwszym przykładzie możemy zobaczyć jak domyślnie kompilator traktuje liczby zmiennoprzecinkowe jako typ double, o ile nie dodamy suffixu f:
W efekcie zamiast operacji zmiennoprzecinkowych VLDR, VDIV itd. mamy w prawym panelu skoki do software’owej emulacji floatów __aeabi_i2d, __aeabi_ddiv.
W drugiej wersji mamy liczbę zmiennoprzecinkową bez suffixu, ale za to po prawej stronie używamy dodatkowej flagi kompilacji -fsingle-precision-constant mówiącej, żeby domyślnie używać float zamiast double:
Jeżeli sprawdzamy takie rzeczy na kompilatorze online, a nie kompilujemy za każdym razem całego projektu, możemy zaoszczędzić dużo czasu. Oczywiście później na docelowym projekcie też warto sprawdzić instrukcje asemblerowe. Ale przynajmniej oszczędzimy sobie kompilacji podczas nieudanych prób wyboru odpowiedniego zestawu flag.
Bardziej zaawansowany przykład
Jeżeli chodzi o optymalizacje, jeszcze większe pole do popisu mamy używając C++. W wielu przypadkach pojedyncza linijka może kryć w sobie wiele operacji asemblerowych. Na przykład o ile robimy kopię zamiast move. Dlatego Compiler Explorer powstał właśnie z myślą o społeczności C++.
Jeżeli chcecie zobaczyć Compiler Explorer w akcji, a przy okazji jak nowoczesny C++ pomaga tworzyć kod zarówno optymalny jak i zrozumiały, polecam poniższą prezentację:
Dodatkowe informacje
Więcej informacji na temat projektu daje nam sam autor – Matt Godbolt:
Tutorial:
Prezentacja o Compiler Explorerze z CppCon: