Halide在归一化互相关期间挂起
Halide hangs during Normalized Cross Correlation
我正在尝试在Halide中实现归一化互相关。
下面的代码构建,Halide JIT编译没有抛出任何错误。然而,Halide似乎在JIT编译后挂起了。无论我在不同的Func上进行了多少次trace_*
调用,都只能打印一个跟踪(在Func output
上):
Begin realization normxcorr.0(0, 2028, 0, 2028)
Produce normxcorr.0(0, 2028, 0, 2028)
任何建议都会有帮助。
该算法等效于OpenCV中的CV_TM_CCOEFF_NORMED和MATLAB:中的normxcorr2
void normxcorr( Halide::ImageParam input,
Halide::ImageParam kernel,
Halide::Param<pixel_t> kernel_mean,
Halide::Param<pixel_t> kernel_var,
Halide::Func& output )
{
Halide::Var x, y;
Halide::RDom rk( kernel );
// reduction domain for cumulative sums
Halide::RDom ri( 1, input.width() - kernel.width() - 1,
1, input.height() - kernel.height() - 1 );
Halide::Func input_32( "input32" ),
bounded_input( "bounded_input"),
kernel_32( "kernel32" ),
knorm( "knorm" ),
conv( "conv" ),
normxcorr( "normxcorr_internal" ),
sq_sum_x( "sq_sum_x" ),
sq_sum_x_local( "sq_sum_x_local" ),
sq_sum_y( "sq_sum_y" ),
sq_sum_y_local( "sq_sum_y_local" ),
sum_x( "sum_x" ),
sum_x_local( "sum_x_local" ),
sum_y( "sum_y" ),
sum_y_local( "sum_y_local" ),
win_var( "win_var" ),
win_mean( "win_mean" );
Halide::Expr ksize = kernel.width() * kernel.height();
// accessing outside the input image always returns 0
bounded_input( x, y ) = Halide::BoundaryConditions::constant_exterior( input, 0 )( x, y );
// cast to 32-bit to make room for multiplication
input_32( x, y ) = Halide::cast<int32_t>( bounded_input( x, y ) );
kernel_32( x, y ) = Halide::cast<int32_t>( kernel( x, y ) );
// cumulative sum along each row
sum_x( x, y ) = input_32( x, y );
sum_x( ri.x, ri.y ) += sum_x( ri.x - 1, ri.y );
// sum of 1 x W strips
// (W is the width of the kernel)
sum_x_local( x, y ) = sum_x( x + kernel.width() - 1, y );
sum_x_local( x, y ) -= sum_x( x - 1, y );
// cumulative sums of the 1 x W strips along each column
sum_y( x, y ) = sum_x_local( x, y );
sum_y( ri.x, ri.y ) += sum_y( ri.x, ri.y - 1);
// sums up H strips (as above) to get the sum of an H x W rectangle
// (H is the height of the kernel)
sum_y_local( x, y ) = sum_y( x, y + kernel.height() - 1 );
sum_y_local( x, y ) -= sum_y( x, y - 1 );
// same as above, just with squared image values
sq_sum_x( x, y ) = input_32( x, y ) * input_32( x, y );
sq_sum_x( ri.x, ri.y ) += sq_sum_x( ri.x - 1, ri.y );
sq_sum_x_local( x, y ) = sq_sum_x( x + kernel.width() - 1, y );
sq_sum_x_local( x, y ) -= sq_sum_x( x - 1, y );
sq_sum_y( x, y ) = sq_sum_x_local( x, y );
sq_sum_y( ri.x, ri.y ) += sq_sum_y( ri.x, ri.y - 1);
sq_sum_y_local( x, y ) = sq_sum_y( x, y + kernel.height() - 1 );
sq_sum_y_local( x, y ) -= sq_sum_y( x, y - 1 );
// the mean value of each window
win_mean( x, y ) = sum_y_local( x, y ) / ksize;
// the variance of each window
win_var( x, y ) = sq_sum_y_local( x, y ) / ksize;
win_var( x, y) -= win_mean( x, y ) * win_mean( x, y );
// partially normalize the kernel
// (we'll divide by std. dev. at the end)
knorm( x, y ) = kernel_32( x, y ) - kernel_mean;
// convolve kernel and the input
conv( x, y ) = Halide::sum( knorm( rk.x, rk.y ) * input_32( x + rk.x, y + rk.y ) );
// calculate normxcorr, except scaled to 0 to 254 (for an 8-bit image)
normxcorr( x, y ) = conv( x, y ) * 127 / Halide::sqrt( kernel_var * win_var( x, y ) ) + 127;
// after scaling pixel values, it's safe to cast down to 8-bit
output( x, y ) = Halide::cast<pixel_t>( normxcorr( x, y ) );
}
我认为这里的问题很简单,因为您没有为任何函数指定任何时间表,所以所有内容都是内联的,导致中间值的大量冗余计算。因此,事实上,这并不是技术上的挂起,而是简单地为每个像素做大量的工作,因此无法在合理的时间内完成。
首先,试着说每个函数都应该是compute_root
(例如,sum_x.compute_root();
),最容易在函数末尾的块中。这应该以更合理的速度进行,应该一个接一个地打印每个函数(从输入开始),而不仅仅是normxcore.0
,并且应该完成。
事实上,您的许多函数实际上只是其输入的逐点转换,因此这些输入可以保持内联(而不是compute_root
),这应该会进一步加快速度(尤其是在您开始对某些阶段进行并行化和矢量化之后)。乍一看,[sq_]sum_{x,y}
可能不应该内联,但其他所有内容可能都可以内联。knorm
和input_32
是一个悬而未决的问题,这取决于你的目标和你的日程安排。
我抛出了一个快速可运行的修订,添加了这个琐碎的时间表,以及其他一些小的清理,在这里:
https://gist.github.com/d64823d754a732106a60
在我的测试中,它在不到一秒钟的时间内以2K^2的输入运行,没有任何花哨的东西。
顺便说一句,一个小提示:使用调试符号(-g
)编译生成器代码应该可以使您在所有Func
声明中都不必提供名称字符串。在早期的实现中,这是一个不幸的缺点,但我们现在可以直接从C++源符号名称中合理地设置这些名称,只要您在启用调试符号的情况下进行编译。
- 挂起和取消挂起一个文件DLL
- 如何防止C++遗留代码中的挂起指针
- 为什么所有C++编译器都会崩溃或挂起此代码
- 我编写了代码将十进制分数转换为其二进制等效数.它编译得很好,但在执行时挂起
- 构建挂起,即使是适度的文件大小
- 循环挂起迭代的 std::擦除 on std::list
- Poco::Net::FTPClientSession 在 open() 方法上挂起 129 秒,如果 ftp 主机不存
- Node.js fs.open() 在尝试打开 4 个以上的命名管道 (FIFO) 后挂起
- 从不同进程中的另一个线程挂起/恢复线程或进程
- Boost (Beast) websocket:同步写入挂起
- 为什么析构函数挂起
- 使用互斥会挂起程序
- 在C++和 Python 程序中使用命名管道的 IPC 挂起
- 设置变量时C++程序挂起
- 第一次尝试使用new动态创建结构数组,程序挂起没有错误
- 从stdin读取时子进程挂起(fork/dup2竞争条件)
- 有时ShowWindow从不调用OnShowWindow,主应用程序挂起
- 如何防止GUI挂起,同时允许第二次操作与Qt中的第一次操作一起执行
- WTSFreeMemory在启动期间从服务调用时挂起-我应该省略吗
- Halide在归一化互相关期间挂起