Я начинаю с Halide, и хотя я понял основные принципы его дизайна, я борюсь с деталями (читай: магия) требуется, чтобы эффективно планировать вычисления.
Ниже я написал MWE об использовании Halide для копирования массива из одного места в другое. Я предполагал, что это скомпилирует всего лишь несколько инструкций и займет меньше микросекунды для запуска. Вместо этого он производит 4000 строк сборки и требует 40 мс для запуска! Очевидно, поэтому у меня есть существенная дыра в моем понимании.
Halide::Image
?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
Позвольте мне начать с вашего второго вопроса: _copy
занимает много времени, потому что нужно скомпилировать код Halide в машинный код 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.
Чтобы подчеркнуть первое, что упомянул Флориан, я думаю, что это ключевой момент недопонимания: вы, похоже, рассчитываете время составления 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.cpp https://github.com/halide/Halide/blob/master/tutorial/lesson_10_aot_compilation_run.cpp