2015-04-30 8 views
6

Ich arbeite an einem C++ - Projekt, das OpenCL verwendet. Ich benutze die CPU als ein OpenCL-Gerät mit der intel OpenCL runtimeWeird OpenCL ruft Nebeneffekt auf C++ für Schleife Leistung

Ich bemerkte einen seltsamen Nebeneffekt beim Aufruf von OpenCL-Funktionen. Hier ist ein einfacher Test:

#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); 

} 

Es ist eine einfache Schleife, die eine Datei char von char liest und fügt sie so dass der Compiler nicht versucht, es zu optimieren aus.

Mein System ist ein Core i7-4770K, 2 TB HDD 16 GB DDR3 Ubuntu 14.10. Das obige Programm mit einer Datei von 100 MB als Eingabe benötigt ungefähr 770 ms. Dies entspricht meiner HDD-Geschwindigkeit. So weit, ist es gut.

Wenn Sie jetzt die Kommentare invertieren und nur die OpenCL-Aufrufregion ausführen, dauert es etwa 200 ms. Nochmal so weit, so gut.

Buf, wenn Sie alle auskommentieren, dauert das Programm mehr als 2000ms. Ich würde 770ms + 200ms erwarten, aber es sind 2000ms. Sie können sogar eine erhöhte Verzögerung zwischen den Ausgabenachrichten in der for-Schleife bemerken. Die beiden Regionen (OpenCL-Aufrufe und Lesezeichen) sollen unabhängig sein.

Ich verstehe nicht, warum die Verwendung von OpenCL mit einer einfachen C++ for-Schleife Leistung stört. Es ist keine einfache OpenCL-Initialisierungsverzögerung.

Ich Kompilieren dieses Beispiel mit:

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

Ich habe auch versucht Clang ++ verwenden, aber es passiert das gleiche.

+0

Ich habe das gleiche unter OS X mit g ++. 0,012s nur für das erste, 14,447s nur für das zweite und dann 14,874s für beide. Es muss etwas über das Öffnen der Befehlswarteschlange für die CPU sein. – sabreitweiser

Antwort

3

Dies war ein interessanter. Das liegt daran, dass getc zu dem Zeitpunkt, an dem die Warteschlange instanziiert wird, eine threadsafe-Version erstellt hat. Daher ist die Zeitersparnis der Grab-Release-Zyklus der Sperren - ich weiß nicht, warum/wie dies geschieht, aber das ist der entscheidende Punkt auf dem AMD OpenCL SDK mit Intel CPUs. Ich war ziemlich erstaunt, dass ich im Wesentlichen die gleichen Zeiten wie OP hatte.

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

Sie können nur die Änderung getc getc_unlocked Abhilfe für dieses spezifische Problem versuchen.

Es brachte es zurück auf 930 ms für mich - dieser Zeitanstieg über 750ms wird hauptsächlich in Plattform und Kontext-Erstellung Linien ausgegeben.

+0

Jetzt verwende ich fgetc_unlocked. Problem gelöst! Mit dieser Funktion dauert es jetzt 344ms. Ich weiß, dass ich die Datei nur von einem Thread lesen werde, also löst das das Problem perfekt für mich. Gute Antwort. Tolle Gemeinschaft. Danke! – pinguino

1

Ich glaube, dass der Effekt von den OpenCL-Objekten verursacht wird, die immer noch im Geltungsbereich sind und deshalb nicht vor der for-Schleife gelöscht werden. Sie können die anderen Berechnungen aufgrund von erforderlichen Überlegungen beeinflussen. Zum Beispiel läuft das Beispiel, wie haben Sie es die folgenden Zeiten auf meinem System (g ++ 4.2.1 mit O2 auf Mac OSX) ergibt:

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

Aber setzt den OpenCL-Code in den eigenen anonymen Umfang, also automatisch aufrufe Die Destruktoren vor den Loops scheinen das Problem zu lösen. Mit dem Code:

#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); 
} 

ich die Zeiten erhalten:

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

die linear hinzuzufügen scheint. Der Effekt ist im Vergleich zu anderen Auswirkungen auf das System, z. B. CPU-Auslastung von anderen Prozessen, ziemlich gering, scheint jedoch beim Hinzufügen des anonymen Bereichs verschwunden zu sein. Ich mache ein Profiling und füge es als Bearbeitung hinzu, wenn es etwas Interessantes hervorbringt.

+0

Ich habe Ihren Code in meinem System versucht. Ich habe es mit AMD OpenCL versucht. Ich habe sogar Kubuntu 15.04 neu installiert, aber es ist dasselbe. Der Nebeneffekt besteht weiter. Ich habe auch einen Mac, und obwohl es sehr langsam ist, fügen Sie die Ausführungszeiten einfach wie erwartet hinzu. Jetzt denke ich, dass es mit der OpenCL-Implementierung für Linux zu tun hat. – pinguino

+0

Interessant. Ich konnte nichts Interessantes aus dem Profiling herausholen. – sabreitweiser