当前位置: 首页 > 工具软件 > Cilk > 使用案例 >

cilk之User Guide学习笔记(2)Cilk关键字预定义宏环境变量

时经纬
2023-12-01

说明:

下载User Guide: http://software.intel.com/zh-cn/forums/showthread.php?t=77996&o=a&s=lr(Cilk_User_Guide.pdf)

主要是对该用户指南(中文版)的一些学习笔记和简化并更加自己的理解添加一些代码示例,可以参考原文档获取更多细节。


1. cilk_spawn

2. cilk_sync

3. cilk_for

4. 预定义宏__cilk

5. 环境变量NWORKERS


1. cilk_spawn

Cilk的核心是三个关键字::_Cilk_spawn, _Cilk_sync 和 _Cilk_for。头文件<cilk/cilk.h>提供了简化的关键字的宏定义(cilk_spawn, cilk_sync 和 cilk_for),一般在代码中用简化的关键字。

cilk_spawn 关键字修饰函数调用语句,以告知Cilk运行系统该函数可能(但不是必须)和调用者并行执行。

简单理解,spawn的中文翻译为“衍生”,这个关键字表明了其修饰的函数可能和当前执行的代码(strand)并行执行,即"衍生"出一个新的线程。后面会更详细的讨论关于"衍生"的执行模型和需要注意的地方。

cilk_spawn的三种形式:

type var = cilk_spawn func(args); //func()有返回值
var = cilk_spawn func(args); // func()有返回值
cilk_spawn func(args); // func()没有返回值

三种形式很容易理解,func就是被修饰的将可能与当前执行的代码并行的函数,三种形式和普通的函数调用形式完全一致,只是加入了关键字cilk_spawn修饰即可。

一个被衍生的函数被称为衍生它的函数的 子任务。相反的,执行cilk_spawn语句的函数被称为是被衍生的函数的 父任务

衍生函数可以使用任意的函数表达形式,但不能在另一个函数的参数中使用cilk_spawn语句,例子:

var = cilk_spawn (object.*pointer)(args); // 使用函数指针或者成员函数指针

g(cilk_spawn f()); // 不允许,函数参数使用cilk_spawn语句。

解决方法是衍生一个函数来调用f()和g(),如:cilk_spawn [&]{ g(f()); }();(使用c++的lambda表达式完成)

注意:这种方法和cilk_spawn g(f());不同,这样f()是在衍生g()以前在父任务中执行的,而在lamda表达方式中,f()和g()都是在子任务中执行。


2. cilk_sync

cilk_sync语句表示当前函数不能继续和衍生的子任务并行执行。在所有子任务执行完之后,当前函数才能继续执行。

语法:cilk_sync;

说明:很显然,sync的含义“同步”,在大多数并行编程都有这样的一个概念,cilk_sync就是一个"等待"的含义,等待当前任务(父任务)所衍生的子任务完成之后才继续往下执行。

需要注意的是:cilk_sync只同步由该函数衍生的子任务。其他函数的子任务不受影响。PS:关于这一点,其实是显然的了,那么一个容易想到的问题是,如果a衍生了b,b衍生了c,在a里面sync,肯定会同步b,那么是否会等待c呢?这是显然的,会。因为每一个spawn的函数结束处都有一个隐式的sync,如果a会等待b,那么b结束前c肯定也结束了。

cilk_sync在有些情况下可能是隐式存在的,其中一种隐式存在的情况是:在每个包含cilk_spawn的函数和try块的结尾,存在隐式的cilk_sync。其原因如下:

确保程序资源的使用不会超出程序并行的部分.
确保无竞争的并行程序能与相应的串行程序具有同样的行为. 一个普通的无衍生的函数调用,不管在被调用的函数内部是否存在衍生的情况下,执行的行为都是一样的。
没有可能产生副作用或没有释放资源的剩余的strands继续执行.
被调用的函数在返回时已完成所有的操作。


实例1:理解cilk_spawn和cilk_sync基本作用,如下:

// File: test1.cpp
#include <stdio.h>
#include <windows.h>
#include <cilk/cilk.h>
#define M	10
int m=0, s=0;

int subtask()
{
	for(int i=0;i<M/2;i++)
	{
		printf("This is sub  task, i=%d, m=%d, s=%d\n", i, m,s);
		Sleep(1);
	}
	return 1;
}

int maintask()
{
	for(int i=0;i<M;i++)
	{
		printf("This is main task, i=%d, m=%d, s=%d\n", i,m,s);
		Sleep(1);
	}
	return 1;
}

int main()
{
	printf("Main strand started...\n");
	s=cilk_spawn subtask();
	printf("Main task started...\n");
	m=maintask();
	cilk_sync;	// 隐式存在
	return 0;
}
编译:icc test1.cpp


3. cilk_for

cilk_for 循环用于取代常规的C/C++ for循环,它允许循环迭代并行执行。其使用形式和for一样,如下:

cilk_for (declaration; conditional expression; increment expression)
	body
注意:cilk_for的串行化就是将cilk_for换成for的结果。因此,一个cilk_for的循环必须是一个合法的C/C++ for循环,但是cilk_for循环有比C/C++ 的for循环更多的限制(参考手册有哪些限制)。

因为循环体要并行执行,循环控制标量和非本地变量均不能被修改,否则将导致数据竞争。

关于cilk_for的限制和更多理解,内容比较复杂。参考手册和后面的说明。


4. 预处理器__cilk

这个宏是由编译器自动定义的,并设置成Cilk语言扩展的版本号.


5. 环境变量CILK_NWORKERS

指定工作线程workers的数量。

 类似资料: