2015-06-26 47 views
0

我開始使用Halide,雖然我已經掌握了其設計的基本原理,但我正在努力處理需要高效的(讀取:魔術)時間表計算。C++數組到Halide圖像(和後面)

我已經發布了使用Halide將數組從一個位置複製到另一個位置的MWE。我曾認爲這可以編譯成只有少數指令,並且運行時間不到一微秒。相反,它會產生4000行裝配,需要40ms運行!因此,顯然,我的理解存在一個重大漏洞。

  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的輸出並分析代碼來做到這一點。我也使用Halide::Generator提供的AOT編譯,這確保我只測量代碼的運行時間而不是編譯時間。

你的另一個問題是,如何包裝Halide::Image中的現有數組。我不這樣做,主要是因爲我使用AOT編譯。但是,內部Halide使用稱爲buffer_t的類型來處理與圖像相關的所有內容。還有叫做Halide::Buffer的C++包裝,使得使用buffer_t更容易一些,我認爲它也可以用於Func::realize而不是Halide::Image。重點是:如果你瞭解buffer_t,你幾乎可以把所有東西都包裝成Halide可消化的東西。

1

爲了強調Florian提到的第一件事,我認爲這是誤解的關鍵點:您似乎是在編制copy操作(「管道」,以常見的Halide術語)的編制時間,而不僅僅是執行。您的代碼大小估計也可能是由copy.cpp產生的整個二進制文件,而不僅僅是Halide生成的copy函數中的代碼(它實際上甚至不會出現在與clang一起編譯的二進制文件中,因爲它僅由在此程序中運行時進行JITing)。

您可以通過realize之前先調用copy.compile_jit()realize隱式調用compile_jit這是第一次運行,所以它是沒有必要的,但它是有價值的因素除了編譯開銷運行時),在這裏觀察你的管道的實際成本。然後你會把你的計時器專門用在realize左右。

如果你真的想預編譯這個(或任何其他)管道靜態鏈接到你的終極程序,這是你可能會期待的,你真正想要做的是在一個程序中使用Func::compile_to_file編譯併發出代碼(如copy.hcopy.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