Parallel processing 如何使用OMP任务测量OMP时间&;递归任务工作负载?
下面我将尝试绘制使用OpenMP任务并行化的代码 在主功能中,启动并行环境,然后立即将代码包装到Parallel processing 如何使用OMP任务测量OMP时间&;递归任务工作负载?,parallel-processing,task,openmp,parallelism-amdahl,Parallel Processing,Task,Openmp,Parallelism Amdahl,下面我将尝试绘制使用OpenMP任务并行化的代码 在主功能中,启动并行环境,然后立即将代码包装到#pragma omp master部分。在计算了预期的工作负载后,根据该工作负载是否低于给定的阈值,必须完成的工作将传递给串行函数或递归拆分工作负载并初始化单独任务的函数。每个#pragma omp taskwait指令后,将聚合每个#pragma omp taskwait任务的结果 int main() { #pragma omp parallel { #pragm
#pragma omp master
部分。在计算了预期的工作负载后,根据该工作负载是否低于给定的阈值,必须完成的工作将传递给串行函数或递归拆分工作负载并初始化单独任务的函数。每个#pragma omp taskwait
指令后,将聚合每个#pragma omp taskwait
任务的结果
int main() {
#pragma omp parallel
{
#pragma omp master
{
//do some serial stuff
//estimate if parallelisation is worth it.
const int workload = estimateWorkload();
if (workload < someBound) {
serialFunction();
}
else {
parallelFunction(workload);
}
}
}
}
int parallelFunction(int workload) {
if (workload < someBound) {
return serialFunction();
}
int result1, result2;
#pragma omp task shared(result1)
{
result1 = parallelFunction(workload/2);
}
#pragma omp task shared(result2)
{
result2 = parallelFunction(workload/2);
}
#pragma omp taskwait
return result1 < result2;
}
intmain(){
#pragma-omp并行
{
#pragma-omp-master
{
//做一些连续的事情
//估计并行化是否值得。
const int workload=estimateWorkload();
if(工作负载
在这样的设置中,如何度量每个线程的实际计算时间
如果我测量CPU时间并且有k
活动线程,那么我将得到k*wallTime
,这是有意义的,因为线程是由前导的#pragma omp parallel
指令初始化的,并且始终保持活动状态。然而,这并没有给我任何关于线程实际工作时间的信息,这使得代码很难分析
Q:在这种设置下,我如何测量每个线程的实际计算时间
一个简单、半手动代码执行时间评测的简单模型:
不用说,对于“嘈杂”的执行平台,CLOCK\u monotic
的选择可以保存漂移的时间更新,但不会“保存”CPU内核外的等待状态,这是由于O/S调度的任何(如果更重的话)后台进程(干扰)
然而,对于原型阶段,这比安装所有“omp本机”回调要容易得多,{ompt\u回调任务\u创建\u t、ompt\u回调任务\u调度\u t、ompt\u回调任务\u依赖性\u t、ompt\u回调调度\u t、ompt\u回调同步\u区域\u t、…、ompt\u回调线程\u开始\u t、ompt\u回调线程\u结束\u t、…/code>处理
副作用奖金:
如果报告和后处理记录的嵌套代码执行各自的持续时间,则琐碎的代码允许“框架”相关调用签名和递归嵌套相关开销的隐藏成本
然后,当此代码开始在与开销相关的部分(加上工作单元的潜在原子性)上丢失时,系统会停止欺骗您,并开始更精确地向您显示-[SERIAL]
-将成本添加到任何预期的True-[PARALLEL]
-部分(s) 加速(期望利用更多(那些和只有那些免费的)资源)
这始终是战争中最艰难的部分(前方还有待战斗……)
OpenMP提供了一个接口,允许外部评测工具在特定事件发生时连接和接收事件。诸如“英特尔VTune”之类的程序可以向您显示所需信息。通过编程,您可以(ab-)根据自己的需要使用。omp_get_wtime()返回挂钟时间;还有许多评测器也可以提供帮助(例如英特尔上的VTune)。我们不必总是重新发明控制盘:-)
EFFICIENCY of SCHEDULING & OCCUPIED RESOURCES' of a CALL to 2-ary task-SCHEDULED fun() with hidden 1-ary RECURSION:
CALL
42----*--------------------------------------------------------------------------------------*
: | |
: | 21----*---------------------------------------*
: | : | |
: | : | 10----*----------------*
: | : | : | |
: | : | : | 5----*----*
: | : | : | : | |
: | : | : | : | 2<
: | : | : | : 2< /
: | : | : 5----*----* 5___/___/................ #taskwait 2
: | : | : : | | /
: | : | : : | 2< /
: | : | : : 2< / /
: | : | : 5___/___/ /
: | : | 10___/____________/............................. #taskwait 5
: | : 10----*----------------* /
: | : : | | /
: | : : | 5----*----* /
: | : : | : | | /
: | : : | : | 2< /
: | : : | : 2< / /
: | : : 5----*----* 5___/___/ /
: | : : : | | / /
: | : : : | 2< / /
: | : : : 2< / / /
: | : : 5___/___/ / /
: | : 10___/____________/__________/.......................................................... #taskwait 10
: | 21___/
: 21----*---------------------------------------* /
: : | | /
: : | 10----*----------------* /
: : | : | | /
: : | : | 5----*----* /
: : | : | : | | /
: : | : | : | 2< /
: : | : | : 2< / /
: : | : 5----*----* 5___/___/ /
: : | : : | | / /
: : | : : | 2< / /
: : | : : 2< / / /
: : | : 5___/___/ / /
: : | 10___/____________/ /
: : 10----*----------------* / /
: : : | | / /
: : : | 5----*----* / /
: : : | : | | / /
: : : | : | 2< / /
: : : | : 2< / / /
: : : 5----*----* 5___/___/ / /
: : : : | | / / /
: : : : | 2< / / /
: : : : 2< / / / /
: : : 5___/___/ / / /
: : 10___/____________/__________/ /
: 21___/_______________________________________________________/...................................................................................................................... #taskwait 21
42___/
RET_/
#include <time.h>
int estimateWorkload() {
return 42; // _________________________________________________________ mock-up "workload"
}
int serial_a_bit_less_naive_factorial( int n ){
return ( n < 3 ) ? n : n * serial_a_bit_less_naive_factorial( n - 1 );
}
int serialFunction() {
return serial_a_bit_less_naive_factorial( 76 );
}
int parallelFunction( int workload, const int someBound ) { // __ pass both control parameters
struct timespec T0, T1;
int retFlag,
retValue,
result1,
result2;
retFlag = clock_gettime( CLOCK_MONOTONIC, &T0 ); // \/\/\/\/\/\/\/\/\/\ SECTION.begin
if ( workload < someBound ) {
retValue = serialFunction();
}
else { // -- [SEQ]----------------------------------------------------
#pragma omp task shared( result1 ) // -- [PAR]|||||||||||||||||||| with (1-ary recursions)
{
result1 = parallelFunction( (int) workload / 2, someBound ); // (int) fused DIV
}
#pragma omp task shared( result2 ) // -- [PAR]|||||||||||||||||||| with (1-ary recursions)
{
result2 = parallelFunction( (int) workload / 2, someBound ); // (int) fused DIV
}
#pragma omp taskwait
retValue = result1 < result2;
}
retFlag = clock_gettime( CLOCK_MONOTONIC, &T1 ); // \/\/\/\/\/\/\/\/\/\ SECTION.end
// ____________________________________________________________________ MAY ADD ACCUMULATION (1-ary recursions)
// ...
// ____________________________________________________________________ MAY ADD ACCUMULATION (1-ary recursions)
return retValue;
}
int main() {
const int someBound = 3; // _______________________________________ a control parameter A
#pragma omp parallel
{
#pragma omp master
{
// -- [SEQ]---------------------------------------- do some serial stuff
// ------------------------------estimate if parallelisation is worth it
const int workload = estimateWorkload();
if ( workload < someBound ) {
serialFunction();
}
else {
parallelFunction( workload, someBound ); // -- [PAR]||||||| with (1-ary recursions)
}
}
}
}