用于矩阵运算的 OpenCV GPU 库有多好?
Posted
技术标签:
【中文标题】用于矩阵运算的 OpenCV GPU 库有多好?【英文标题】:How good is OpenCV GPU library for matrix operations? 【发布时间】:2012-06-30 23:54:10 【问题描述】:我正在将 OpenCV 用于计算机视觉中的应用程序。我想在 GPU 上加速一些矩阵运算(矩阵相当大),如果可能的话,我想避免直接在 CUDA C 中编码。 OpenCV 2.4.1 有许多 GPU 加速功能。他们在您的体验中表现如何?我最好改用另一个库(例如 Thrust)吗?
编辑 示例应用程序:Calculate squared Euclidean distance matrix on GPU。目前,我使用并行计算工具箱 (PCT) 在 Matlab 中的 GPU 加速(和矢量化)实现比我使用 OpenCV 的 C++ 实现快大约 5-10 倍。
Matlab 实现:
function K = sqEuclideanDist(P_cpu,Q_cpu)
% Vectorized method to compute pairwise squared Euclidean distance on GPU
% Returns K(i,j) = (P(i,:) - Q(j,:))'*(P(i,:) - Q(j,:))
P_gpu = gpuArray(P_cpu);
Q_gpu = gpuArray(Q_cpu);
[nP, d] = size(P_gpu);
[nQ, d] = size(Q_gpu);
pmag = sum(P_gpu .* P_gpu, 2);
qmag = sum(Q_gpu .* Q_gpu, 2);
% note that K is on GPU
K = ones(nP,1)*qmag' + pmag*ones(1,nQ) - 2*P_gpu*Q_gpu';
end
更新 这是另一个完成相同任务的 Matlab 实现(感谢 https://***.com/a/7774323/1121420)。但它只在 CPU 上运行,因为 PCT 不支持bsxfun
。不过仍在寻找 C++ 替代品。
function K = sqEuclideanDist(P_cpu,Q_cpu)
% Returns K(i,j) = (P(i,:) - Q(j,:))'*(P(i,:) - Q(j,:))
% Runs on CPU only.
K = bsxfun(@plus,sum(p.^2,2),sum(q.^2,2)') - 2*(p*q');
end
【问题讨论】:
您特别考虑使用哪些功能? 基本矩阵的东西。 gpu::reduce,gpu::multiply(每元素矩阵乘法)。还有,矩阵乘法,求矩阵特征值和特征向量,矩阵转置。 @Alex - 所有简单的矩阵运算都直接使用 NVidia(推力?)库,因此得到了很好的优化 可能是我的c++代码不够优化.. @Alex,你能发布你的 MATLAB/PCT 代码吗?如果是这样,我将能够为您发布一些有用的代码。 【参考方案1】:我发现ArrayFire 要快得多,并且已经开始使用它代替 OpenCV 中的 GPU 内核进行图像处理。这里是some benchmarks,我发现将 ArrayFire(以前位于名为 LibJacket 的不同界面)与 OpenCV 进行比较,并且在我的基准测试中也确实如此,ArrayFire 比 OpenCV 中的 GPU 函数快 2-4 倍。据我所知,NVIDIA 没有在 OpenCV 中编写 GPU 内核,而是将其外包给了某人,这可能就是它们如此缓慢的原因。由于我只使用 1 个 GPU,我可以免费使用 ArrayFire。
更新,鉴于@Alex 发布的新 MATLAB 代码:我在我的系统上运行了此代码的基准测试。我知道 Parallel Computing Toolbox gpuArray 比 CPU 慢,但 Jacket 和 ArrayFire 却很糟糕。硬件规格是:
Intel(R) Xeon(R) CPU X5660 @ 2.80GHz
NVIDIA Tesla M2090
使用 Parallel Computing Toolbox gpuArray(完全预热)的 CPU 与 GPU 的结果。 CPU 比 PCT gpuArray 快:
>> tic; sqEuclideanDist(gpuArray(rand(1581,3)),gpuArray(rand(189,3))); toc;
Elapsed time is 0.006859 seconds.
>> tic; sqEuclideanDist(rand(1581,3),rand(189,3)); toc;
Elapsed time is 0.005712 seconds.
使用 Jacket(完全预热)的 CPU 与 GPU 的结果。 Jacket 比 PCT gpuArray 高出 3.7 倍,比 CPU 高出 3 倍
>> tic; sqEuclideanDist(gdouble(rand(1581,3)),gdouble(rand(189,3))); toc;
Elapsed time is 0.001876 seconds.
以下是修改后的代码,让您可以轻松运行:
function K = sqEuclideanDist(P,Q)
% Vectorized method to compute pairwise squared Euclidean distance on GPU
% Returns K(i,j) = (P(i,:) - Q(j,:))'*(P(i,:) - Q(j,:))
[nP, d] = size(P);
[nQ, d] = size(Q);
pmag = sum(P .* P, 2);
qmag = sum(Q .* Q, 2);
K = ones(nP,1)*qmag' + pmag*ones(1,nQ) - 2*P*Q';
end
Jacket 确实支持 GPU 上的 BSXFUN,它确实在一定程度上提高了速度:
>> tic; sqEuclideanDist(gdouble(rand(1581,3)),gdouble(rand(189,3))); toc;
Elapsed time is 0.001420 seconds.
请注意,此处使用的尺寸非常小,因此大多数尝试在这些小尺寸上运行的 CUDA 代码可能会表现不佳。这就是为什么我喜欢使用 AccelerEyes 的东西,因为这些家伙已经优化了 GPU,不像 PCT gpuArray、Thrust、OpenCV,每个我过去都尝试过。
这是 ArrayFire 免费 C++ 结果:
Time: 0.0003577 seconds
Speedups: 19.2X faster than PCT gpuArray, 16X faster than the CPU, 5.2X faster
than Jacket in MATLAB original version, 4X faster than Jacket in MATLAB using
BSXFUN
这是我为此编写的 ArrayFire 代码:
static array SqEuclideanDist(array P, array Q)
// 0 based indexing
array pmag = sum(P * P, 1);
array qmag = sum(Q * Q, 1);
int np = P.dims(0);
int nq = Q.dims(0);
array K = tile(qmag.T(), np, 1) + tile(pmag, 1, nq) - 2 * matmul(P, Q.T());
return K;
int main(int argc, char **argv)
double *P_cpu = new double[1581 * 3];
double *Q_cpu = new double[189 * 3];
array P = array(1581, 3, P_cpu);
array Q = array(189 , 3, Q_cpu);
af::sync();
int iter = 1000;
timer::tic();
for (int i = 0; i < iter; i++)
array K = SqEuclideanDist(P, Q);
af::eval(K);
af::sync();
printf("Time taken: %2.4lfms\n", (1000 * timer::toc()) / iter);
delete[] P_cpu;
delete[] Q_cpu;
【讨论】:
干得好。感谢您提供替代方案。今天确实学到了一些东西:不知道Jacket对bsxfun的支持,我喜欢ArrayFire的简单代码。唯一的问题是——尽管有一个免费版本的 ArrayFire C++ 库,但免费版本提供的功能非常有限(例如,它不支持线性代数运算)。我正在寻找一个开源库,你能推荐一下吗? 不客气。令人惊讶的是,有多少人对这篇文章投了反对票。可能是 MathWorks 的员工。 不幸的是,没有一个开源库可以提供非常好的性能。这就是我一直在使用 ArrayFire 的原因,因为至少它对于我需要的东西是免费的。 ArrayFire 中的几乎所有函数都是免费的,除了那些来自 CULA 的函数,它在线性代数方面比 MAGMA 更好。但是 ArrayFire 确实有免费的单精度线性代数函数,我经常使用。这对你有用吗?顺便说一句,您发布的代码没有使用那些线性代数功能。 是的,我不确定是否有反对票,希望人们解释他们的理由。我为我的应用程序尝试了带有 Jacket 的 Matlab,它确实提供了比 PCT 更好的性能(大约 3 倍加速)。看看我是否可以使用免费版的 C++ ArrayFire 来获得更多性能。 我不完全确定发生了什么,但我尝试了 R 上的 ArrayFire 代码的直接端口(使用 RcppArrayfire),它不会输出对角线上为 0 的距离矩阵,即使很小矩阵(所以我不认为这是一个数学近似错误)。也许某些功能的工作方式发生了变化?【参考方案2】:它们是由 NVidia 贡献的,因此在 CUDA 兼容卡上具有良好的性能。 实际性能取决于卡本身和您使用的功能。
根据我的经验,只有 cvRotate 和 cvResize 比普通的 Intel cpu 具有更好的性能。 (注:我只对图片相关的功能感兴趣)
【讨论】:
以上是关于用于矩阵运算的 OpenCV GPU 库有多好?的主要内容,如果未能解决你的问题,请参考以下文章
GPU可以用于Android Environmement上的数值计算(复矩阵乘法)吗?
使用 opencv 在 GPU 上调整图像大小会产生空输出矩阵