2015-06-26 3 views
0

Я начинаю с Halide, и пока я понял основные принципы его дизайна, я борюсь с подробностями (читай: magic) требуется для эффективного расписание вычислений.C++ массив в Halide Image (и обратно)

Я опубликовал ниже MWE с использованием Halide для копирования массива из одного места в другое. Я предположил, что это скомпилирует только до нескольких инструкций и займет менее микросекунды для запуска. Вместо этого он производит 4000 линий сборки и занимает 40 мс для запуска! Очевидно, поэтому у меня есть существенная дыра в моем понимании.

  1. Что такое канонический способ обертывания существующего массива в Halide::Image?
  2. Каким образом следует выполнить функцию copy, чтобы выполнить копию?

Минимального рабочего пример

#include <Halide.h> 

using namespace Halide; 

void _copy(uint8_t* in_ptr, uint8_t* out_ptr, const int M, const int N) { 

    Image<uint8_t> in(Buffer(UInt(8), N, M, 0, 0, in_ptr)); 
    Image<uint8_t> out(Buffer(UInt(8), N, M, 0, 0, out_ptr)); 

    Var x,y; 
    Func copy; 
    copy(x,y) = in(x,y); 
    copy.realize(out); 
} 

int main(void) { 
    uint8_t in[10000], out[10000]; 
    _copy(in, out, 100, 100); 
} 

компиляция Флаги

clang++ -O3 -march=native -std=c++11 -Iinclude -Lbin -lHalide copy.cpp 

ответ

2

Позволь мне начать с вашим вторым вопросом: _copy занимает много времени, потому что он должен собрать галоидный код для машинного кода x86. IIRC, Func кэширует машинный код, но поскольку copy является локальным для _copy, этот кеш нельзя использовать повторно. В любом случае, планирование copy довольно просто, потому что это поточечная операция: во-первых, было бы, вероятно, иметь смысл ее векторизовать. Во-вторых, может иметь смысл распараллелить его (в зависимости от количества данных). Например:

copy.vectorize (x, 32) .parallel (y);

будет векторизовать вдоль x с векторным размером 32 и распараллеливание вдоль y. (Я делаю это из памяти, может быть некоторая путаница в правильных именах.) Конечно, все это может также увеличить время компиляции ...

Рецепта для хорошего планирования не существует. Я делаю это, смотря на вывод compile_to_lowered_stmt и профилируя код. Я также использую компиляцию AOT, предоставленную Halide::Generator, это гарантирует, что я измеряю только время выполнения кода, а не время компиляции.

Ваш другой вопрос: как обернуть существующий массив в Halide::Image. Я этого не делаю, главным образом потому, что я использую компиляцию AOT. Однако внутри Halide использует тип, называемый buffer_t для всего изображения. Существует также C++-обертка, называемая Halide::Buffer, что делает использование buffer_t немного проще, я думаю, что он также может использоваться в Func::realize вместо Halide::Image. Дело в том, что, если вы понимаете, что buffer_t вы можете перенести почти все на что-то, что можно усвоить Halide.

1

Чтобы подчеркнуть первое, на что упомянул Флориан, который, по моему мнению, является ключевым моментом недоразумения здесь: вы, по-видимому, выбираете компиляцию операции copy («конвейер», в общих терминах Halide), а не только его выполнение.Ваша оценка размера кода предположительно также для всего двоичного файла, полученного в результате copy.cpp, а не только кода в генерируемой Halide copy (которая вообще не будет отображаться в двоичном файле, который вы компилируете с помощью clang, поскольку он построен только по JITing во время выполнения в этой программе).

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

Если вы на самом деле хотите предварительно скомпилировать этот (или любой другой) конвейер для статической привязки к вашей конечной программе, что, кажется, вы ожидаете, то, что вы действительно хотите сделать, это использовать Func::compile_to_file в одной программе для скомпилируйте и испустите код (как copy.h и copy.o), а затем соедините их и вызовите в другой программе. Проверьте учебник урок 10, чтобы увидеть это более подробно:

https://github.com/halide/Halide/blob/master/tutorial/lesson_10_aot_compilation_generate.cpphttps://github.com/halide/Halide/blob/master/tutorial/lesson_10_aot_compilation_run.cpp