Test 2 – biblioteka standardowa i nie tylko
W tym teście zostały przetestowane std::cin, std::cout, std::sort i prosta implementacja ROT47. Szkielet kodu – czyli wczytywanie danych – wygląda tak:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> #include <string> using namespace std; int main() { string tmp, str; while( getline(cin,tmp) ) str += tmp + '\n'; cout << "Some first characters: " << str.substr(0,50) << '\n'; return 0; } |
W teście 2a i 2b (zob. nazewnictwo w paczce), czyli testy ROT47 oraz std::sort po pętli while dodane są odpowiednie kawałki kodu:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
// ROT47 register char * a = const_cast< char * >( str.c_str() ); register char * b = a + str.length(); for( ; a != b; *a = ( *a <= '~'/*126*/ && *a >= '!'/*33*/ ? ( *a < 'P'/*80*/ ? *a += 47 : *a -= 47 ) : *a ), a++ ); // std::sort register char * a = const_cast< char * >( str.c_str() ); register char * b = a + str.length(); sort(a,b,[](char a, char b){return a<b;}); // Edycja: // powyższy sposób pobierania - mimo tego, iż - przynajmniej u mnie - działa, // to jest to UB. U siebie możesz (a nawet powinieneś!) użyć: char* a = &str[0]; // ERRATA: w nowym teście poprawiłem to - by inni też mogli sobie skompilować to bez UB |
W przypadku testu 2c, czyli testu std::cout, linijkę:
1 |
cout << "Some first characters: " << str.substr(0,50) << '\n'; |
zamieniamy na:
1 |
cout << "Full read text: " << str << '\n'; |
czyli po prostu wypisujemy całą zawartość stringu. Test został wykonany na dwóch przypadkach testowych, z czego pierwszy ważył ok. 657 KiB, a drugi ok. 11.15 MiB – czyli zdecydowanie więcej. Oto one:
Kompilator | Wczytywanie danych (std::cin+getline+string) [ms] | ROT47 [ms] | std::sort [ms] | std::cout [ms] |
Test 2.1 – dane wejściowe ~657 KiB | ||||
MSVC | 37 | 5 | 24 | 322 |
GCC | 364 | 3 | 42 | 5 |
Test 2.2 – dane wejściowe ~11.15 MiB | ||||
MSVC | 629 | 68 | 377 | 5472 |
GCC | 6174 | 58 | 864 | 80 |
Hmm, jak widać MSVC pozytywnie nas zaskoczyło. Przy wczytywaniu danych MSVC ma miażdżącą przewagę, jednakże przy wypisywaniu danych – przegrywa z kretesem. W przypadku std::sort jest ok. dwukrotnie szybsze, natomiast przy własnej implementacji ROT47 – GCC jest minimalnie szybszy.
Z powyższych wykresów wolno wyciągnąć kilka wniosków (pisząc, że GCC/MSVC coś robi mam na myśli programy skompilowane nimi):
- std::cin w wersji MSVC działa ok. 2 razy szybciej niż ten w wersji GCC
- ROT47 jest ok. 3 razy szybszy w wersji GCC
- std::sort w bibliotece standardowej GCC jest średnio 3 razy szybszy
- std::cout ma miażdżącą przewagę w wersji GCC – w tych testach średnio 70-krotną
Wiem, iż takie pojedyncze testowane części nie są zbyt miarodajnym testem, więc postanowiłem uczynić krok dalej. Próbowałem skompilować dotychczasowo mój największy projekt (z czasów gimnazjum :P), ok. 9k+ LoC (wszystkie linie) – Mrowqa Text Coder (zob. „Gry i aplikacje -> Desktopowe”) – jednakże nie udało mi się. Pomijając niektóre sprawy uniemożliwiające kompilację MTC natrafiłem na coś dziwnego (tak, wiem, mój kod, mój błąd, ale MSVC to skompilował):
1 2 3 4 5 6 7 |
class szyfr { // ... string & operator[]( unsigned int który ) { return który < ileRazySzyfrowane ? kod_szyfru[ który ] : NULL; } // gdzie: string* kod_szyfru; }; |
Ciekawe referencję do czego by zwrócił MSVC, gdyby miał ją wziąć z NULLa… Może kiedyś sprawdzę w ramach nowego wpisu jako ciekawostkę :). Po tym błędzie (i paru innych) odechciało mi się kompilacji MTC przez GCC (defaultowo MTC jest skompilowane przez MSVC). Nadal pozostaje jeden problem – w miarę wiarygodny benchmark, więc postanowiłem…
Ze skryptu kompilującego:
cl /FA %file%.%ext% -o %file%_msvc
%gcc% -O2 -std=c++11 %file%.%ext% -o %file%_gcc.exe
Czyli porównywane jest GCC z -O2 oraz MSVC z /O0. Takie porównanie nie ma sensu.
Ten tekst diametralnie zmieni swoją postać, gdy włączysz optymalizacje dla kompilatora MSVC. Obecnie porównujesz kod GCC optymalizowany do kodu MSVC z WYŁĄCZONĄ optymalizacją.
Dzięki za uwagę, testy przeprowadziłem ponownie i poprawiłem wyniki na stronie 🙂
Jeszcze jedno – porównujesz jakość kontenera std:list, a nie jakość optymalizacji kodu przez kompilatory. std::list jest beznadziejny w MSVC, więc… porównujesz dwa różne kody źródłowe i w zasadzie jest to co najwyżej ‚benchmark’ kontenera std::list oraz innych użytych narzędzi. Kiedyś sporo benchmarków robiłem różnych kontenerów i std::list pod VC++ jest na tyle beznadziejny, że należy go stosować tylko i wyłącznie tam, gdzie jest konieczność ‚wstawiania’ danych w środek iterowanej listy.
Jeszcze link do jednego z przeprowadzonych testów:
http://cpp0x.pl/forum/temat/?id=4004