2014-12-04 15 views
5

Niedawno miałem za zadanie wykonanie kilku kontroli prędkości, dzięki czemu mogę stwierdzić, czy szybciej użyć php/php-cli lub C++ do wstawienia pewnej liczby wierszy do bazy danych.Wstawianie MySQL, szybsze w PHP niż C++, jest to oczekiwane?

Zanim zaczniemy, powiem wam kilka szczegółów, więc wszystko jest jasne:

  • Część php jest prowadzony przez Apache, wymagane bezpośrednio w przeglądarce.
  • Testy dysku twardego są uruchamiane na dysku SSD. Sądzę, że w zwykłych napędach rzeczy będą wolniejsze. Maszyna sama w sobie nie jest niczym specjalnym, mniej więcej sześcioletnim.
  • Wszystkie wkładki są wykonywane za pomocą przygotowanych instrukcji. Używamy mysqli na php i mysqlcppconn (mysql C++, dostarczone przez Oracle).
  • Wszystkie wkładki są gotowe do wprowadzenia po wprowadzeniu. Wiem, że możemy je układać, ale dobrze, testujemy tutaj.
  • Czasy są wyświetlane za pomocą mikrotomów w php i poprzez nagłówek w C++.
  • Sam kod nie jest równoważny, oczywiście. Więcej o tym później.
  • Cały tekst jest w formacie UTF-8. Tam jest rosyjski, chiński, arabski, hiszpański, angielski i wszelkiego rodzaju szalone rzeczy. Tabela mysql znajduje się w utf8_4mb.
  • Liczby dla kodu C++ są wynikiem użycia poziomów std :: vector i -O2 kompilujących się z g ++ (wektory lepsze od map, unordered_maps i std :: arrays).

Tak, jest to proces:

  • Połącz się z bazą danych.
  • Otwórz plik tekstowy za pomocą N wierszy.
  • Przeczytaj wiersz pliku.
  • Podziel linię na znak separatora.
  • Użyj pewnych części podzielonej linii, aby uzyskać wartości wstawiania (np. Indeksy 0, 1 i 3).
  • Wyślij te części do przygotowanej instrukcji, aby je wstawić.
  • Powtarzaj, aż plik zostanie całkowicie przeczytany.

Oba kody działają dokładnie zgodnie z oczekiwaniami. Oto wynikające numery:

php:

  • 5000 wpisów: 1,42 - 1,27 sek.
  • 20000 wpisów: 5,53 - 6,18 sek.
  • 50000 wpisów: 14,43 - 15,69 sek.

C++:

  • 5000 wpisów: 1,78 - 1,81 sek.
  • 20000 wpisów: 7,19 - 7,22 sek.
  • 50000 wpisów: 18,52 - 18,84 sek.

php wyprzedza C++, gdy linie w pliku rosną ... Początkowo podejrzewałem o funkcję dzielenia linii: dzielenie w php odbywa się za pomocą "explode". Algorytm jest tak naiwny jak dla C++ ... Kontener jest przekazywany przez referencję, a jego zawartość zmienia się w locie. Kontener jest przesuwany tylko raz. Upewniłem się, że pojemnik "rezerwuje()" wszystkie niezbędne miejsca (pamiętaj, ostatecznie wybrałem wektory), które zostały naprawione. Kontener jest tworzony na głównej funkcji, a następnie przekazywany przez odniesienie za pomocą kodu. Nigdy nie jest opróżniany ani zmieniany: zmienia się tylko jego zawartość.

template<typename container> void explode(const std::string& p_string, const char p_delimiter, container& p_result) 
{ 
    auto it=p_result.begin(); 
    std::string::const_iterator beg=p_string.begin(), end=p_string.end(); 
    std::string temp; 

    while(beg < end) 
    { 
     if((*beg)==p_delimiter) 
     { 
      *(it)=temp; 
      ++it; 
      temp=""; 
     } 
     else 
     { 
      temp+=*beg; 
     } 

     ++beg; 
    } 

    *(it)=temp; 
} 

Jak powiedziano wcześniej, wykonywane zadanie jest równoważne, ale kod generujący go nie jest. Kod C++ ma standardowe bloki try-catch do kontrolowania interakcji mysql. Co do reszty, główna pętla działa aż do osiągnięcia EOF, a każda iteracja sprawdza, czy wstawienie nie powiodło się (zarówno w języku C++, jak i php).

Widziałem C++ znacznie przewyższające php w pracy z plikami i ich zawartością, więc spodziewałem się, że to samo będzie miało zastosowanie tutaj. Jakoś podejrzewam o algorytm podziału, ale może po prostu jest to, że złącze do bazy danych jest wolniejsze (nadal, gdy wyłączam interakcję z bazą danych, php jest nadal przetwarzane szybciej) lub mój kod jest podrzędny ...

Jeśli chodzi o profilowanie, gprof wypluł to się o kod C++:

Each sample counts as 0.01 seconds. 
    % cumulative self    self  total   
time seconds seconds calls ns/call ns/call name  
60.00  0.03  0.03 50000 600.00 600.00 void anc_str::explotar_cadena<std::vector<std::string, std::allocator<std::string> > >(std::string const&, char, std::vector<std::string, std::allocator<std::string> >&) 
40.00  0.05  0.02        insertar(sql::PreparedStatement*, std::string const&, std::vector<std::string, std::allocator<std::string> >&) 
    0.00  0.05  0.00  1  0.00  0.00 _GLOBAL__sub_I__ZN7anc_str21obtener_linea_archivoERSt14basic_ifstreamIcSt11char_traitsIcEE 

Gdzie „explotar_cadena” jest „eksploduje” i „insertar” jest „podzielony tę linię i ustaw przygotowane oświadczenie w górę”. Jak widać 60% czasu tam spędzamy (nic dziwnego ... działa 50000 razy i robi to szalone dzielenie rzeczy). "obtener_linea_archivo" to po prostu "proszę, zrzuć następną linię do ciągu".

Bez mysql interakcji (wystarczy wczytać plik, czytać wiersze i podzielić je) uzyskać te pomiary:

php

  • 5000 wpisów: 0,019 - 0,036 sek.
  • 20000 wpisów: 0,09 - 0,10 sek.
  • 50000 wpisów: 0,14 - 0,17 sek.

C++

  • 5000 wpisów: 0,07 - 0,10 sek.
  • 20000 wpisów: 0,25 - 0,26 sek.
  • 50000 wpisów: 0,49 - 0,55 sek.

Dobra, oba razy są dobre i prawie niezauważalne dla prawdziwych warunków życia, jestem zaskoczony ... Więc pytanie brzmi: czy mam się tego spodziewać? Ktoś, kto ma wcześniejsze doświadczenie, gotów pomóc?

Z góry dziękuję.

Edytuj: Oto krótki link do obcinanej wersji zawierającej pliki wejściowe, kod C++ i kod php [http://www.datafilehost.com/d/d31034d6]. Zauważ, że nie ma interakcji sql: tylko otwieranie pliku, dzielenie łańcucha i mierzenie czasu. Proszę, wybaczcie zmasakrowany kod i pół hiszpańskie komentarze i nazwy zmiennych, ponieważ zrobiono to w pośpiechu. Zwróć też uwagę na wyniki gprof powyżej: nie jestem ekspertem, ale myślę, że staramy się znaleźć lepszy sposób podziału łańcucha.

+0

Czy możesz poprosić o przetestowanie programu C++ za pomocą [Very Sleepy] (http://www.codersnotes.com/sleepy) i tutaj dodaj wyniki. –

+0

Nie osoba z Windows tutaj, przepraszam ... Próbowałem gprof. Zmodyfikuje post, aby to odzwierciedlić. –

+1

Usuń liczniki z kodu, użyj polecenia czasu systemu na konsoli, aby uzyskać pomiar. Nie wliczasz czasu uruchamiania i zamykania PHP, pochopne wyniki. –

Odpowiedz

1

Pewna jego część może mieć związek ze sterownikiem/interfejsem używanym w każdym języku. Na przykład, z PHP/MySQL prawdopodobnie okaże się, że mysqli jest szybszy niż mysql, który jest szybszy niż PDO. Dzieje się tak dlatego, że biblioteki są coraz bardziej abstrakcyjne (lub mniej konserwowane). Możesz spróbować profilowania samych zapytań na serwerze bazy danych, aby sprawdzić, czy występują różnice w czasie wykonywania. Z drugiej strony może być więcej, jak zauważyli inni komentatorzy.

+0

Bardzo dziękuję za odpowiedź. Myślę, że mogę założyć, że sama implementacja ma więcej warstw, tym samym jest wolniejsza. Mimo to, bez interakcji z bazą danych uzyskuję lepsze liczby za pomocą php (jak widać w edytowanym pytaniu). Jak już wspomniano, kod C++ wydaje się generować znacznie więcej instrukcji niż prostszy kod php. –

Powiązane problemy