2015-04-30 9 views
6

Pracuję nad projektem C++ za pomocą OpenCL. Używam procesora jako urządzenia OpenCL z Zauważyłem dziwny efekt uboczny podczas wywoływania funkcji OpenCL. Oto prosty test:Weird OpenCL wywołuje efekt uboczny w C++ dla wydajności pętli

#include <iostream> 
#include <cstdio> 
#include <vector> 
#include <CL/cl.hpp> 

int main(int argc, char* argv[]) 
{ 
    /* 
     cl_int status; 
    std::vector<cl::Platform> platforms; 
    cl::Platform::get(&platforms); 
    std::vector<cl::Device> devices; 
    platforms[1].getDevices(CL_DEVICE_TYPE_CPU, &devices); 
    cl::Context context(devices); 
    cl::CommandQueue queue = cl::CommandQueue(context, devices[0]); 
    status = queue.finish(); 
    printf("Status: %d\n", status); 
*/ 

    int ch; 
    int b = 0; 
    int sum = 0; 
    FILE* f1; 
    f1 = fopen(argv[1], "r"); 

    while((ch = fgetc(f1)) != EOF) 
    { 
    sum += ch; 
    b++; 
    if(b % 1000000 == 0) 
     printf("Char %d read\n", b); 
    } 
    printf("Sum: %d\n", sum); 

} 

To prosta pętla który odczytuje plik char przez char i dodaje je więc kompilator nie starają się optymalizować ją.

Mój system to Core i7-4770K, 2 TB HDD 16 GB DDR3 z systemem Ubuntu 14.10. Powyższy program, z plikiem 100 MB jako wejściem, zajmuje około 770 ms. Jest to zgodne z moją szybkością HDD. Jak na razie dobrze.

Jeśli teraz odwrócisz komentarze i uruchomisz tylko region połączeń OpenCL, zajmie to około 200ms. Znowu, jak dotąd, tak dobrze.

Buf, jeśli odkomentujesz wszystko, program zajmuje więcej niż 2000 ms. Spodziewałbym się 770ms + 200ms, ale to 2000ms. Można nawet zauważyć zwiększone opóźnienie między komunikatami wyjściowymi w pętli for. Dwa regiony (wywołania OpenCL i znaki do czytania) powinny być niezależne.

Nie rozumiem, dlaczego korzystanie z OpenCL zakłóca prostą wydajność pętli C++. To nie jest proste opóźnienie inicjowania OpenCL.

Mam kompilacji ten przykład z:

g++ weird.cpp -O2 -lOpenCL -o weird 

Próbowałem też za pomocą szczęk ++, ale zdarza się to samo.

+0

Mam to samo na OS X z g ++. 0,012s dla pierwszego tylko, 14,447 dla drugiego tylko, a następnie 14,874 dla obu. Musi to być coś na temat otwierania kolejki poleceń do procesora. – sabreitweiser

Odpowiedz

3

To było interesujące. Dzieje się tak dlatego, że getc jest tworzony w trybie wątków w momencie tworzenia kolejki, a więc zwiększenie czasu to cykl chwytania blokad - nie mam pewności, dlaczego/jak to się dzieje, ale jest to decydujący punkt w AMD OpenCL SDK z procesorami Intel. Byłem bardzo zaskoczony, że miałem zasadniczo tyle samo co OP.

https://software.intel.com/en-us/forums/topic/337984

Można spróbować remedium na tego konkretnego problemu, po prostu zmieniając getc do getc_unlocked.

Sprowadził mnie z powrotem do 930 ms - ten czas ponad 750 ms spędziłem głównie w liniach tworzenia platform i kontekstów.

+0

Teraz używam fgetc_unlocked. Problem rozwiązany! Dzięki tej funkcji zajmuje teraz 344 ms. Wiem, że będę czytał plik tylko z jednego wątku, więc to doskonale rozwiązuje problem dla mnie. Świetna odpowiedź. Niesamowita społeczność. Dzięki Ci! – pinguino

1

Wierzę, że efekt jest spowodowany przez obiekty OpenCL nadal w zakresie, a zatem nie są usuwane przed pętli for. Mogą mieć wpływ na inne obliczenia z powodu potrzebnych rozważań. Na przykład, uruchamiając przykład jak dałeś to daje następujące czasy w moim systemie (g ++ 4.2.1 z O2 na Mac OSX):

CL: 0.012s 
Loop: 14.447s 
Both: 14.874s 

Ale wprowadzenie kodu OpenCL pod własnym zakresie anonimowy, dlatego automatycznie dzwoni destruktory przed pętlami zdają się pozbywać problemu. Przy użyciu kodu:

#include <iostream> 
#include <cstdio> 
#include <vector> 
#include "cl.hpp" 

int main(int argc, char* argv[]) 
{ 

    { 
    cl_int status; 
    std::vector<cl::Platform> platforms; 
    cl::Platform::get(&platforms); 
    std::vector<cl::Device> devices; 
    platforms[1].getDevices(CL_DEVICE_TYPE_CPU, &devices); 
    cl::Context context(devices); 
    cl::CommandQueue queue = cl::CommandQueue(context, devices[0]); 
    status = queue.finish(); 
    printf("Status: %d\n", status); 
    } 

    int ch; 
    int b = 0; 
    int sum = 0; 
    FILE* f1; 
    f1 = fopen(argv[1], "r"); 
    while((ch = fgetc(f1)) != EOF) 
    { 
     sum += ch; 
     b++; 
     if(b % 1000000 == 0) 
      printf("Char %d read\n", b); 
    } 
    printf("Sum: %d\n", sum); 
} 

dostaję taktowania:

CL: 0.012s 
Loop: 14.635s 
Both: 14.648s 

co wydaje się dodać liniowo. Efekt jest dość mały w porównaniu z innymi efektami w systemie, takimi jak obciążenie procesora z innych procesów, ale wydaje się, że zniknął podczas dodawania anonimowego zakresu. Zrobię profilowanie i dodaję go jako edycję, jeśli wygeneruje coś interesującego.

+0

Wypróbowałem twój kod w moim systemie. Próbowałem z AMD OpenCL. Zrobiłem nawet nową instalację Kubuntu 15.04, ale to jest to samo. Efekt uboczny utrzymuje się. Mam też Maca i chociaż jest bardzo wolny, czasy wykonania po prostu dodam, zgodnie z oczekiwaniami. Teraz myślę, że ma to związek z implementacją OpenCL dla systemu Linux. – pinguino

+0

Interesujące. Nie udało mi się uzyskać nic ciekawego z profilowania. – sabreitweiser