前言
上一篇介绍到双边滤波的效能比OpenCV
差了四倍之多,然而是因为OpenCV
使用了迴圈的平行运算
,因此今日笔记做个简单的平行运算用法,这里主要使用自己想法解释,对理论有兴趣可前往微软或网上有丰富的介绍。
C++ for
这里使用双指标double
做走访动作,大小1000 * 1000运行500次,而运行时间如下图,需要约30
秒。
int main(){ size_t size = 1000;double** values = new double*[size];double start = 0.0;double end = 0.0;for (size_t index = 0; index < size; index++){values[index] = new double[size];}start = clock();for (size_t time = 0; time < 500; time++){for (size_t row = 0; row < size; row++){for (size_t col = 0; col < size; col++){values[row][col] = (row * col - sin(row) + cos(col)) * exp(row / (col + 1));}}}end = clock();cout << "运行时间:" << (end - start) / CLOCKS_PER_SEC << endl; for (size_t index = 0; index < size; index++){delete[] values[index];values[index] = nullptr;}delete[] values;values = nullptr;}
C++ parallel_for
平行运算主要是分配给其他处理器去做处理,而微软提供了一个函式库ppl
,Intel提供了一个函式库tbb
,这边主要使用ppl来做介绍。详细使用可去爬文,这边直接使用简单实例了解。
1.引入函式库。
#include <ppl.h>
2.呼叫函数parallel_for(起始, 结尾, lambda运算式)。
concurrency::parallel_for(0, 500, [&](const int& time) {for (size_t row = 0; row < size; row++){for (size_t col = 0; col < size; col++){values[row][col] = (row * col - sin(row) + cos(col)) * exp(row / (col + 1));}} cout << time << " ";});
这边先加入cout << time << " ";
为了先让我们先理解它每个完成的时间点,如下图,可以看到每个结束时间不是依序的,有的甚至在" "
还没输出时另一个已经输出完成点,而这就是它快速的原因。
两者时间比较
下图为两者速度比较,可以看到平行运算所花的时间比一般迴圈来的快许多,这也就能解释为何昨日介绍的双边滤波速度慢的原因了。
双边滤波实测
使用400x400大小指标阵列,sigma
均为30,滤版大小21x21,模拟运行50次,结果如下图,只需要2.4s
,与昨日相比快了3~4倍,但为了要让C#
使用dll
我们将专案性质转为支援clr
,而在clr
是不允行include
与线程(thread
)相关的函式库。
int main(){C_UINT32 width = 400;C_UINT32 height = 400;C_FLOAT spaceSigma = 30.0f;C_FLOAT colorSigma = 30.0f;C_UINT32 size = 21;C_UCHAE* src = new C_UCHAE[width * height];UCHAE* pur = new UCHAE[width * height];Library lib;double start = 0.0;double end = 0.0;start = clock();for (size_t index = 0; index < 50; index++){lib.BilateralBlur8bit(src, pur, width, height, spaceSigma, colorSigma, size);}end = clock();cout << " BilateralBlur8bit 运行时间:" << (end - start) / CLOCKS_PER_SEC << endl;delete[] src;delete[] pur;}
C#呼叫方式
在C#
也有平行做法,以下模拟5000x5000运行500次运算,一般速度约40s
,Parallel
速度约8s
,但在计算量小的时候,使用Parallel
反而会增加负担,这点必须切记。
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();const int size = 5000;string result = "";stopwatch.Reset();stopwatch.Start();for (int time = 0; time < 500; time++){ for (int row = 0; row < size; row++) { for (int col = 0; col < size; col++) { int value = row * col; value = value - row * col; value = row * col; } }}result = stopwatch.Elapsed.TotalMilliseconds.ToString();stopwatch.Reset();stopwatch.Start();Parallel.For (0, 500, (int index) => { for (int row = 0; row < size; row++) { for (int col = 0; col < size; col++) { int value = row * col; value = value - row * col; value = row * col; } }}); result = stopwatch.Elapsed.TotalMilliseconds.ToString();
结语
虽然很可惜无法用在C#
呼叫C++
上面,但也学到了一个新的平行函数,而在C#使用平行函数的成本比C++
来的大,若对计算有要求其实可以不用clr
做法,方法一可以写C++
介面,方法二写C++ Win32
应用程式使用C#
呼叫,我想这也是可行方法之一,但切记关于多线程若没有使用lock
则会随意地执行。
而在平行还有许多函数可以使用有兴趣可以自行去微软教学网站学习,若有问题或文章有误欢迎留言和指导谢谢。
参考网址
[1]https://msdn.microsoft.com/zh-tw/library/dd504906.aspx
[2]https://docs.microsoft.com/zh-tw/dotnet/standard/parallel-programming/how-to-write-a-simple-parallel-for-loop