2015-04-30 3 views
6

Я работаю над проектом C++ с использованием OpenCL. Я использую CPU как устройство OpenCL с intel OpenCL runtimeWeird OpenCL вызывает побочный эффект на C++ для производительности цикла

Я заметил странный побочный эффект при вызове функций OpenCL. Вот простой тест:

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

} 

Это простой цикл, который читает файл символ на гольца и добавляет их, так что компилятор не пытается оптимизировать его.

Моя система Core i7-4770K, 2TB HDD 16GB DDR3 работает Ubuntu 14.10. Программа выше, с файлом 100 Мбайт в качестве входных данных, занимает около 770 мс. Это соответствует скорости моего жесткого диска. Все идет нормально.

Если вы теперь инвертируете комментарии и запускаете только зону вызовов OpenCL, она занимает около 200 мс. Опять же, до сих пор, так хорошо.

Buf, если вы раскомментируете все, программа занимает более 2000 мс. Я ожидал бы 770 мс + 200 мс, но это 2000 мс. Вы даже можете заметить увеличение задержки между выходными сообщениями в цикле for. Предполагается, что два региона (вызовы OpenCL и считывающие символы) являются независимыми.

Я не понимаю, почему использование OpenCL мешает простой работе C++ for loop. Это не простая задержка инициализации OpenCL.

Я компиляция этого примера с:

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

Я также попытался с помощью Clang ++, но это происходит то же самое.

+0

Я получил то же самое на OS X с g ++. 0.012s только для первого, 14.447s для второго только, а затем 14.874s для обоих. Это должно быть чем-то вроде открытия очереди команд в CPU. – sabreitweiser

ответ

3

Это было интересно. Это связано с тем, что getc становится потоковой версией в момент, когда очередь создается экземпляром, и поэтому увеличение времени - это цикл блокировки блокировки блокировок - я не уверен, почему/как это происходит, но это решающий момент для AMD OpenCL SDK с процессорами Intel. Я был очень удивлен, что имел в основном то же время, что и ОП.

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

Вы можете попробовать средство для этой конкретной проблемы, просто изменив ЕОКП к getc_unlocked.

Он вернул мне до 930 мс для меня - это увеличение времени более 750 мс в основном проводится в линиях создания платформы и контекста.

+0

Теперь я использую fgetc_unlocked. Задача решена! Теперь эта функция занимает 344 мс. Я знаю, что буду читать файл только из одного потока, так что это отлично решает проблему для меня. Отличный ответ. Удивительное сообщество. Спасибо вам! – pinguino

1

Я считаю, что эффект вызван тем, что объекты OpenCL все еще находятся в области видимости и поэтому не удаляются перед циклом for. Они могут влиять на другие вычисления из соображений, необходимых. Например, запустив пример, как вы дали это дает следующие раз на моей системе (г ++ 4.2.1 с O2 на Mac OSX):

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

Но ввод кода OpenCL в свою анонимную сферу, поэтому автоматически вызывая деструкторы перед петлями, похоже, избавляются от проблемы. Использование кода:

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

я получаю тайминги:

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

который, кажется, добавить линейно. Эффект довольно мал по сравнению с другими эффектами в системе, такими как загрузка процессора из других процессов, но, похоже, он исчезнет при добавлении анонимной области. Я сделаю некоторое профилирование и добавлю его как редактирование, если оно произведет что-нибудь интересное.

+0

Я пробовал свой код в своей системе. Я пробовал с AMD OpenCL. Я даже сделал новую установку Kubuntu 15.04, но это то же самое. Побочный эффект сохраняется. У меня тоже есть Mac, и хотя он очень медленный, время выполнения просто добавляет, как и ожидалось. Теперь я думаю, что это связано с реализацией OpenCL для Linux. – pinguino

+0

Интересно. Я не смог получить ничего интересного из профилирования. – sabreitweiser

Смежные вопросы