2014-10-08 5 views
1

Я пытаюсь использовать функцию extern в Halide. В моем контексте я хочу сделать это на GPU.Использование extern на галиде с GPU

Я компилирую в компиляции AOT с оператором opencl. Конечно, OpenCL все еще может использовать процессор, поэтому я использую это:

halide_set_ocl_device_type("gpu"); 

В настоящем время, все расписание на compute_root().

Первый вопрос, если я использую compute_root() и OpenCL gpu, мой процесс будет вычисляться на устройстве с помощью некоторых CopyHtoD и DtoH? (Или он будет на хост-буфере)

Второй вопрос, больше связанных с функциями extern. Мы используем какой-то внешний вызов, потому что некоторые из наших алгоритмов не в Halide. Экстерн вызов:

foo.define_extern("cool_foo", args, Float(32), 4); 

Экстерн получить: ехЬегп "C" INT cool_foo (buffer_t * в, Int W, внутр ч, внутр г, buffer_t * из) {..}

Но, функция cool_foo, мой buffer_t загружается только в память хоста. Адрес dev равен 0 (по умолчанию).

Если я пытаюсь скопировать память до алгоритма:

halide_copy_to_dev(NULL, &in); 

Он ничего не делает.

Если я только память устройства:

in.host = NULL; 

Мой указатель хоста являются пустым, но адрес устройства по-прежнему 0.

(dev_dirty правда в моем случае и host_dirty ложно)

Любая идея?

EDIT (Чтобы ответить dsharlet)

Вот структура моего кода:

данных Разбор правильно на CPU. -> Отправил буфер на графическом процессоре (с помощью halide_copy_to_dev ...) -> Ввести в структуру Halide, прочитать параметр и добавить граничное условие -> Пойти в мою внешнюю функцию -> ...

I не имеют действительного buffer_t в моей функции extern. Я планирую все в compute_root(), но использую HL_TARGET = host-opencl и устанавливаю ocl в gpu. Прежде чем войти в Halide, я могу прочитать адрес своего устройства, и все в порядке.

Вот мой код:

Перед галогенид, все было CPU материал (указатель), и мы TRANSFERT его GPU

buffer_t k = { 0, (uint8_t *) k_full, {w_k, h_k, num_patch_x * num_patch_y * 3}, {1, w_k, w_k * h_k}, {0}, sizeof(float), }; 
#if defined(USEGPU) 
    // Transfer into GPU 
    halide_copy_to_dev(NULL, &k); 
    k.host_dirty = false; 
    k.dev_dirty = true; 
    //k.host = NULL; // It's k_full 
#endif 
halide_func(&k) 

Внутри ДРИ:

ImageParam ... 
Func process; 
process = halide_sub_func(k, width, height, k.channels()); 
process.compute_root(); 

... 

Func halide_sub_func(ImageParam k, Expr width, Expr height, Expr patches) 
{ 
    Func kBounded("kBounded"), kShifted("kShifted"), khat("khat"), khat_tuple("khat_tuple"); 
    kBounded = repeat_image(constant_exterior(k, 0.0f), 0, width, 0, height, 0, patches); 
    kShifted(x, y, pi) = kBounded(x + k.width()/2, y + k.height()/2, pi); 

    khat = extern_func(kShifted, width, height, patches); 
    khat_tuple(x, y, pi) = Tuple(khat(0, x, y, pi), khat(1, x, y, pi)); 

    kShifted.compute_root(); 
    khat.compute_root(); 

    return khat_tuple; 
} 

Вне галоидный (Экстерн функция):

inline .... 
{ 
    //The buffer_t.dev and .host are 0 and null. I expect a null from the host, but the dev.. 
} 
+0

Можете ли вы поделиться кодом, определяющим и планирующим этап до этапа экстерна? Планируется ли это на графическом процессоре? Если нет, я думаю, что поведение, которое вы видите, ожидается. – dsharlet

ответ

0

Знаете ли вы о протоколе вывода границ для функций внешнего массива? Это происходит, когда указатель хоста любого буфера равен NULL. (Вкратце, в этом случае вам нужно заполнить поля степени в структурах buffer_t, у которых есть указатели на NULL, и ничего не делать.) Если вы уже позаботились об этом, то проигнорируйте вышеизложенное.

Если вы протестировали, что указатели хоста не являются NULL для всех буферов, то вызов функции halide_copy_to_dev должен работать. Возможно, вам придется явно установить host_dirty в true, чтобы получить экземпляр копии, в зависимости от того, откуда пришел буфер. (Я бы надеялся, что Halide получит это право, и он уже установлен, если буфер пришел с предыдущего этапа конвейера на CPU.Но если буфер пришел из чего-то вне Halide, грязные биты, вероятно, являются ложными от инициализации. Кажется, halide_dev_malloc должен установить dev_dirty, если он выделяет память устройства, а в настоящее время он этого не делает.)

Я ожидаю, что поле dev будет заполнено после вызова halide_copy_to_dev, поскольку первое, что он делает, это вызвать halide_dev_malloc. Вы можете напрямую вызвать halide_dev_malloc самостоятельно, установив host_dirty, а затем вызовите halide_copy_to_dev.

Является ли предыдущий этап на хосте или на графическом процессоре? Если он находится на графическом процессоре, я бы ожидал, что входной буфер будет на GPU.

Этот API требует работы. Я нахожусь в середине первого рефакторинга кое-чего, что поможет, но в конечном итоге это потребует изменения структуры buffer_t. Можно заставить большинство работать, но для этого требуется изменить хост_dirty и dev_dirty биты, а также вызвать API-интерфейс halide_dev * только в правильном направлении. Спасибо за терпеливость.

+0

Спасибо Залман. Если мой буфер равен NULL, я заполняю поля экстентов и выхожу из extern. Но, если я хочу, чтобы хост был NULL, а Dev получил что-то, что мне нужно сделать? Предыдущий этап моего конвейера (до галида) находится на процессоре, но я хочу использовать свою часть в Halide только на графическом процессоре. Последнее, если я планирую в compute_root и использовать HL_TARGET = host-opencl и выбрать gpu, выполнил бы код на GPU (без оптимизации) или CPU? – Darkjay

3

Я нахожу решение для своей проблемы.

Опубликовать ответ в коде только здесь. (Так как я сделал немного автономный тест, имя переменной не соответствует)

Внутри галогенида: (Halide_func.cpp)

#include <Halide.h> 


using namespace Halide; 

using namespace Halide::BoundaryConditions; 

Func thirdPartyFunction(ImageParam f); 
Func fourthPartyFunction(ImageParam f); 
Var x, y; 

int main(int argc, char **argv) { 
    // Input: 
    ImageParam f(Float(32), 2, "f"); 

    printf(" Argument: %d\n",argc); 

    int test = atoi(argv[1]); 

    if (test == 1) { 
     Func f1; 
     f1(x, y) = f(x, y) + 1.0f; 
     f1.gpu_tile(x, 256); 
     std::vector<Argument> args(1); 
     args[ 0 ] = f; 
     f1.compile_to_file("halide_func", args); 

    } else if (test == 2) { 
     Func fOutput("fOutput"); 
     Func fBounded("fBounded"); 
     fBounded = repeat_image(f, 0, f.width(), 0, f.height()); 
     fOutput(x, y) = fBounded(x-1, y) + 1.0f; 


     fOutput.gpu_tile(x, 256); 
     std::vector<Argument> args(1); 
     args[ 0 ] = f; 
     fOutput.compile_to_file("halide_func", args); 

    } else if (test == 3) { 
     Func h("hOut"); 

     h = thirdPartyFunction(f); 

     h.gpu_tile(x, 256); 
     std::vector<Argument> args(1); 
     args[ 0 ] = f; 
     h.compile_to_file("halide_func", args); 

    } else { 
     Func h("hOut"); 

     h = fourthPartyFunction(f); 

     std::vector<Argument> args(1); 
     args[ 0 ] = f; 
     h.compile_to_file("halide_func", args); 
    } 
} 

Func thirdPartyFunction(ImageParam f) { 
    Func g("g"); 
    Func fBounded("fBounded"); 
    Func h("h"); 
    //Boundary 
    fBounded = repeat_image(f, 0, f.width(), 0, f.height()); 
    g(x, y) = fBounded(x-1, y) + 1.0f; 
    h(x, y) = g(x, y) - 1.0f; 

    // Need to be comment out if you want to use GPU schedule. 
    //g.compute_root(); //At least one stage schedule alone 
    //h.compute_root(); 

    return h; 
} 

Func fourthPartyFunction(ImageParam f) { 
    Func fBounded("fBounded"); 
    Func g("g"); 
    Func h("h"); 

    //Boundary 
    fBounded = repeat_image(f, 0, f.width(), 0, f.height()); 

    // Preprocess 
    g(x, y) = fBounded(x-1, y) + 1.0f; 

    g.compute_root(); 
    g.gpu_tile(x, y, 256, 1); 


    // Extern 
    std::vector <ExternFuncArgument> args = { g, f.width(), f.height() }; 
    h.define_extern("extern_func", args, Int(16), 3); 

    h.compute_root(); 
    return h; 
} 

Внешняя функция: (external_func.h)

#include <cstdint> 
#include <cstdio> 
#include <cstdlib> 
#include <cassert> 
#include <cinttypes> 
#include <cstring> 
#include <fstream> 
#include <map> 
#include <vector> 
#include <complex> 
#include <chrono> 
#include <iostream> 


#include <clFFT.h> // All OpenCL I need are include. 

using namespace std; 
// Useful stuff. 
void completeDetails2D(buffer_t buffer) { 
    // Read all elements: 
    std::cout << "Buffer information:" << std::endl; 
    std::cout << "Extent: " << buffer.extent[0] << ", " << buffer.extent[1] << std::endl; 
    std::cout << "Stride: " << buffer.stride[0] << ", " << buffer.stride[1] << std::endl; 
    std::cout << "Min: " << buffer.min[0] << ", " << buffer.min[1] << std::endl; 
    std::cout << "Elem size: " << buffer.elem_size << std::endl; 
    std::cout << "Host dirty: " << buffer.host_dirty << ", Dev dirty: " << buffer.dev_dirty << std::endl; 
    printf("Host pointer: %p, Dev pointer: %" PRIu64 "\n\n\n", buffer.host, buffer.dev); 
} 

extern cl_context _ZN6Halide7Runtime8Internal11weak_cl_ctxE; 
extern cl_command_queue _ZN6Halide7Runtime8Internal9weak_cl_qE; 


extern "C" int extern_func(buffer_t * in, int width, int height, buffer_t * out) 
{ 
    printf("In extern\n"); 
    completeDetails2D(*in); 
    printf("Out extern\n"); 
    completeDetails2D(*out); 

    if(in->dev == 0) { 
     // Boundary stuff 
     in->min[0] = 0; 
     in->min[1] = 0; 
     in->extent[0] = width; 
     in->extent[1] = height; 
     return 0; 
    } 

    // Super awesome stuff on GPU 
    // ... 

    cl_context & ctx = _ZN6Halide7Runtime8Internal11weak_cl_ctxE; // Found by zougloub 
    cl_command_queue & queue = _ZN6Halide7Runtime8Internal9weak_cl_qE; // Same 

    printf("ctx: %p\n", ctx); 

    printf("queue: %p\n", queue); 

    cl_mem buffer_in; 
    buffer_in = (cl_mem) in->dev; 
    cl_mem buffer_out; 
    buffer_out = (cl_mem) out->dev; 

    // Just copying data from one buffer to another 
    int err = clEnqueueCopyBuffer(queue, buffer_in, buffer_out, 0, 0, 256*256*4, 0, NULL, NULL); 

    printf("copy: %d\n", err); 

    err = clFinish(queue); 

    printf("finish: %d\n\n", err); 

    return 0; 
} 

Наконец, не-ДРИ материал: (Halide_test.cpp)

#include <halide_func.h> 
#include <iostream> 
#include <cinttypes> 

#include <external_func.h> 

// Extern function available inside the .o generated. 
#include "HalideRuntime.h" 

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

    // Init the kernel in GPU 
    halide_set_ocl_device_type("gpu"); 

    // Create a buffer 
    int width = 256; 
    int height = 256; 
    float * bufferHostIn = (float*) malloc(sizeof(float) * width * height); 
    float * bufferHostOut = (float*) malloc(sizeof(float) * width * height); 

    for(int j = 0; j < height; ++j) { 
     for(int i = 0; i < width; ++i) { 
      bufferHostIn[i + j * width] = i+j; 
     } 
    } 

    buffer_t bufferHalideIn = {0, (uint8_t *) bufferHostIn, {width, height}, {1, width, width * height}, {0, 0}, sizeof(float), true, false}; 
    buffer_t bufferHalideOut = {0, (uint8_t *) bufferHostOut, {width, height}, {1, width, width * height}, {0, 0}, sizeof(float), true, false}; 

    printf("IN\n"); 
    completeDetails2D(bufferHalideIn); 
    printf("Data (host): "); 
    for(int i = 0; i < 10; ++ i) { 
     printf(" %f, ", bufferHostIn[i]); 
    } 
    printf("\n"); 

    printf("OUT\n"); 
    completeDetails2D(bufferHalideOut); 

    // Send to GPU 
    halide_copy_to_dev(NULL, &bufferHalideIn); 
    halide_copy_to_dev(NULL, &bufferHalideOut); 
    bufferHalideIn.host_dirty = false; 
    bufferHalideIn.dev_dirty = true; 
    bufferHalideOut.host_dirty = false; 
    bufferHalideOut.dev_dirty = true; 
    // TRICKS Halide to force the use of device. 
    bufferHalideIn.host = NULL; 
    bufferHalideOut.host = NULL; 

    printf("IN After device\n"); 
    completeDetails2D(bufferHalideIn); 

    // Halide function 
    halide_func(&bufferHalideIn, &bufferHalideOut); 

    // Get back to HOST 
    bufferHalideIn.host = (uint8_t*)bufferHostIn; 
    bufferHalideOut.host = (uint8_t*)bufferHostOut; 
    halide_copy_to_host(NULL, &bufferHalideOut); 
    halide_copy_to_host(NULL, &bufferHalideIn); 

    // Validation 
    printf("\nOUT\n"); 
    completeDetails2D(bufferHalideOut); 
    printf("Data (host): "); 
    for(int i = 0; i < 10; ++ i) { 
     printf(" %f, ", bufferHostOut[i]); 
    } 
    printf("\n"); 

    // Free all 
    free(bufferHostIn); 
    free(bufferHostOut); 

} 

Вы можете скомпилировать halide_func с го e test 4, чтобы использовать все функциональные возможности Extern.

Вот некоторые из заключений, которые у меня есть. (Спасибо Zalman и zougloub)

  • Compute_root не вызывается устройством, если вы используете его в одиночку.
  • Нам нужен gpu() gpu_tile() в коде для вызова режима GPU. (BTW, вам нужно поставить всю свою переменную внутрь)
  • gpu_tile les, чем ваш предмет, разбивает ваши вещи.
  • BoundaryCondition хорошо работает в графическом процессоре.
  • Перед вызовом функции extern, Func, который идет как вход, должен быть: f.compute_root(); f.gpu_tile (х, у, ..., ...); Compute_root в средней стадии не является неявным.
  • Если адрес dev равен 0, это нормально, мы повторно отправляем измерение и внешний вызов будет вызываться снова.
  • Последний этап как compute_root() неявный.

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

  • Нет связанных вопросов^_^