<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>中文 &#187; Feilong H. (Intel)</title>
	<atom:link href="http://software.intel.com/zh-cn/blogs/author/feilong-h/feed/" rel="self" type="application/rss+xml" />
	<link>http://software.intel.com/zh-cn/blogs</link>
	<description></description>
	<lastBuildDate>Mon, 28 May 2012 13:40:23 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.3</generator>
		<item>
		<title>英特尔编译器优化系列：深入IPO优化</title>
		<link>http://software.intel.com/zh-cn/blogs/2010/08/16/ipo-2/</link>
		<comments>http://software.intel.com/zh-cn/blogs/2010/08/16/ipo-2/#comments</comments>
		<pubDate>Mon, 16 Aug 2010 07:27:42 +0000</pubDate>
		<dc:creator>Feilong H. (Intel)</dc:creator>
				<category><![CDATA[Blog Challenge]]></category>
		<category><![CDATA[博客征文专栏]]></category>
		<category><![CDATA[并行计算]]></category>
		<category><![CDATA[软件开发工具]]></category>
		<category><![CDATA[Intel Compiler]]></category>
		<category><![CDATA[IPO]]></category>

		<guid isPermaLink="false">http://software.intel.com/zh-cn/blogs/2010/08/16/ipo-2/</guid>
		<description><![CDATA[在上一篇文章《英特尔编译器优化系列：初识IPO优化》中，我们认识了IPO以及IPO的使用步骤。下面让我们进一步深入了解IPO。]]></description>
			<content:encoded><![CDATA[<p>在上一篇文章《<a href="http://software.intel.com/zh-cn/blogs/2010/08/13/ipo/">英特尔编译器优化系列：初识IPO优化</a>》中，我们认识了IPO以及IPO的使用步骤。下面让我们进一步深入了解IPO。</p>
<p><strong>使用IPO</strong><strong>优化大型应用程序</strong></p>
<p>我们知道，IPO在“编译”时将源代码转换成中间语言文件，然后在“链接”时将所有的中间语言文件合并为一个中间语言文件，然后对其进行分析与优化，优化后生成一个的二进制文件，然后传递给链接器进行链接。因此我们会发现“编译”这一步的速度是非常快的，“链接”的速度要比没有使用IPO时慢得多。这里给“编译”和“链接”打上引号，是特指IPO优化步骤中的两个阶段，请参考《<a href="http://software.intel.com/zh-cn/blogs/2010/08/13/ipo/">英特尔编译器优化系列：初识IPO优化</a>》内的“IPO示意图”。</p>
<p>在使用IPO优化大型的应用程序的时候，在“链接”阶段进行IPO优化后生成的单个二进制文件可能会非常庞大，这可能会给系统虚拟内存造成很大的压力，导致频繁的内存换页，最终影响编译、链接的效率，甚至出现out of memory信息。实际上，编译器会根据应用程序的大小来决定要生成一个还是多个二进制文件。通常情况下，编译器的决定是能满足我们的需求的。但是如果你在编译大型应用程序的时候，发现有编译链接时间过长或out of memory，你可以尝试用下面方法之一来解决：</p>
<ul>
<li>在-ipo（Linux和Mac OS X）或/Qipo（Windows）开关后跟一个大于0的整数，如-ipoN或/QipoN。这个N的作用是让编译器生成N个二进制文件。你可以根据具体情况，来调整合适的N。</li>
<li>使用-ipo-separate（Linux和Mac OS X）或/Qipo-separate（Windows）开关。编译器将生成与源文件个数相同的二进制文件。</li>
</ul>
<p><strong>inline行为的控制</strong></p>
<p>通过指定函数的属性，可以控制编译器的inline行为。比如，__forceinline和always_inline让编译器必须将函数inline，而编译器肯定不会inline有noinline属性的函数。</p>
<blockquote><p>__forceinline int add(int a, int b);              // Windows</p>
<p>void foo(int x) __attribute__ ((<code>always_inline</code>));  // Linux &amp; Mac OS X</p>
<p>void bar(int y) __attribute__ ((<code>noinline</code>));       // Linux &amp; Mac OS X</p></blockquote>
<p>一些编译器开关也可以控制编译器的inline行为：</p>
<table style="padding-left: 30px;width: 458px;height: 93px" border="1" cellpadding="0">
<thead>
<tr>
<td width="30%" valign="top"><strong>Option</strong></td>
<td width="70%" valign="top"><strong>Description</strong></td>
</tr>
</thead>
<tbody>
<tr>
<td width="30%" valign="top">-inline-level=0或/Ob0</td>
<td width="70%" valign="top">不inline用户函数。</td>
</tr>
<tr>
<td width="30%" valign="top">-inline-level=1或/Ob1</td>
<td width="70%" valign="top">仅inline那些有inline关键字或inline属性的用户函数。</td>
</tr>
<tr>
<td width="30%" valign="top">-inline-level=2或/Ob2</td>
<td width="70%" valign="top">编译器可以自行决定inline任何函数。默认开关。</td>
</tr>
</tbody>
</table>
<p><strong> </strong></p>
<p><strong>IPO</strong><strong>优化报告</strong></p>
<p>英特尔编译器可以生成各种优化报告，月的这些优化报告对程序性能调优大有益处。</p>
<p>我们看看一下程序：</p>
<blockquote><p>// a.c</p>
<p>int foo(int a)</p>
<p>{</p>
<p>   return a*2;</p>
<p>}</p>
<p>// b.c</p>
<p>int bar(int b)</p>
<p>{</p>
<p>   return b*3;</p>
<p>}</p>
<p>// c.c</p>
<p>int foo(int);</p>
<p>int bar(int);</p>
<p>int main()</p>
<p>{</p>
<p>    return foo(2) + bar(3);</p>
<p>}</p></blockquote>
<p>我们可以分两步完成编译和链接，也可以一步完成，同时打印出IPO优化报告：</p>
<blockquote><p>$ icc -c -ipo ipo a.c b.c c.c</p>
<p>$ icc -ipo -opt-report -opt-report-phase ipo a.o b.o c.o</p></blockquote>
<p>或</p>
<blockquote><p>$ icc -ipo -opt-report -opt-report-phase ipo a.c b.c c.c</p></blockquote>
<p>IPO优化报告如下：</p>
<blockquote><p>ipo: remark #11000: performing multi-file optimizations</p>
<p>&lt;;-1:-1;IPO FUNCTION PROMOTION TO STATIC;;0&gt;</p>
<p>  FUNCTIONS PROMOTED TO STATIC: TOTAL(2)</p>
<p>&lt;;-1:-1;IPO ROUTINE ATTRIBUTES PROPAGATION;;0&gt;</p>
<p>  ROUTINE ATTRIBUTE PROPAGATION TOTALS:</p>
<p>      RDECL: NSE(0-&gt;0), AR(0-&gt;0)</p>
<p>      ENTRY: SE(0-&gt;0), DSE(0-&gt;0), AR(0-&gt;0)</p>
<p>&lt;;-1:-1;IPO MODREF;;0&gt;</p>
<p>  CI-MOD: TOTAL(3): EMPTY(3)</p>
<p>  CI-REF: TOTAL(3): OTHER(3)</p>
<p>  CS-MOD: TOTAL(3): EMPTY(3)</p>
<p>ipo: remark #11005: generating object file /tmp/ipo_iccAzJ3dP.o</p>
<p>&lt;;-1:-1;IPO;;0&gt;</p>
<p>WHOLE PROGRAM (SAFE) [EITHER METHOD]: TRUE</p>
<p>WHOLE PROGRAM (SEEN) [TABLE METHOD]: TRUE</p>
<p>WHOLE PROGRAM (READ) [OBJECT READER METHOD]: TRUE</p>
<p>INLINING OPTION VALUES:</p>
<p>  -inline-factor: 100</p>
<p>  -inline-min-size: 30</p>
<p>  -inline-max-size: 230</p>
<p>  -inline-max-total-size: 2000</p>
<p>  -inline-max-per-routine: disabled</p>
<p>  -inline-max-per-compile: disabled</p>
<p>&lt;c.c;4:5;IPO INLINING;main;0&gt;</p>
<p><strong>INLINING REPORT: (main) [1/3=33.3%]</strong></p>
<p><strong>  -&gt; INLINE: bar(4) (isz = 0) (sz = 6 (2+4))</strong></p>
<p><strong>  -&gt; INLINE: foo(5) (isz = 0) (sz = 6 (2+4))</strong></p>
<p>&lt;a.c;-1:-1;IPO DEAD STATIC FUNCTION ELIMINATION;foo;0&gt;</p>
<p><strong>DEAD STATIC FUNCTION ELIMINATION:</strong></p>
<p><strong>  (foo)</strong></p>
<p><strong>  Routine is dead static</strong></p>
<p>&lt;b.c;-1:-1;IPO DEAD STATIC FUNCTION ELIMINATION;bar;0&gt;</p>
<p><strong>DEAD STATIC FUNCTION ELIMINATION:</strong></p>
<p><strong>  (bar)</strong></p>
<p><strong>  Routine is dead static</strong></p></blockquote>
<p>我们可以看到，函数foo和bar被inline到main函数中了（见报告中第一部分粗体字）。由于程序中没有其他地方调用到foo和bar，foo和bar变成了无人调用的函数，因此这2个函数作为dead static function被删除（见报告中第二、三部分粗体字）。</p>
<p>这个例子中，我们使用icc作为链接器。如果你使用的是xi开头的工具，如xilink、xilib、xild、xiar或xilibtool，你可以用以下开关来生成优化报告。这些开关与我们上面用的到开关只是名字有细微的差别，功能完全一样。</p>
<table border="1" cellspacing="0" cellpadding="0" width="100%">
<thead>
<tr>
<td width="30%" valign="top"><strong>Tool Option</strong></td>
<td width="70%" valign="top"><strong>Description</strong></td>
</tr>
</thead>
<tbody>
<tr>
<td width="30%" valign="top">-qopt-report[=<em>n</em>]</td>
<td width="70%" valign="top">将优化报告输出到stderr。n可以是0到3，表示报告的详细程度。n的缺省值为2。</td>
</tr>
<tr>
<td width="30%" valign="top">-qopt-report-file=<em>file</em></td>
<td width="70%" valign="top">将优化报告输出到文件。</td>
</tr>
<tr>
<td width="30%" valign="top">-qopt-report-phase=<em>name</em></td>
<td width="70%" valign="top">仅生成某个优化名称为name的优化报告。若不指定优化名称，则生成所有优化的优化报告。</td>
</tr>
<tr>
<td width="30%" valign="top">-qopt-report-routine=<em>string</em></td>
<td width="70%" valign="top">仅针对函数名包含string的函数生成优化报告。若不指定，则生成所有函数的优化报告。</td>
</tr>
<tr>
<td width="30%" valign="top">-qopt-report-help</td>
<td width="70%" valign="top">列出所有可以生成优化报告的优化名称。</td>
</tr>
</tbody>
</table>
<p><strong> </strong></p>
<p><strong>一些tips</strong></p>
<ul>
<li>将IPO与其他一些优化一同使用，可以获得更好的优化效果。因为IPO能给其他优化提供必要的信息，使其他优化更高效、更准确。比如，HLO（High-Level Optimization）、PGO（Profile-Guided Optimization）、Vectorization。</li>
<li>在编译C++文件时，英特尔编译器的跨文件inlining默认是打开的。</li>
<li>你可以一部分代码（或模块）使用IPO优化，另一部分代码（或模块）不使用IPO优化，然后把它们链接起来。</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/zh-cn/blogs/2010/08/16/ipo-2/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>英特尔编译器优化系列：初识IPO优化</title>
		<link>http://software.intel.com/zh-cn/blogs/2010/08/13/ipo/</link>
		<comments>http://software.intel.com/zh-cn/blogs/2010/08/13/ipo/#comments</comments>
		<pubDate>Fri, 13 Aug 2010 03:06:11 +0000</pubDate>
		<dc:creator>Feilong H. (Intel)</dc:creator>
				<category><![CDATA[Blog Challenge]]></category>
		<category><![CDATA[博客征文专栏]]></category>
		<category><![CDATA[并行计算]]></category>
		<category><![CDATA[软件开发工具]]></category>
		<category><![CDATA[Intel Compiler]]></category>
		<category><![CDATA[IPO]]></category>

		<guid isPermaLink="false">http://software.intel.com/zh-cn/blogs/2010/08/13/ipo/</guid>
		<description><![CDATA[本文介绍英特尔编译器提供的IPO优化，讲解IPO与其他优化的区别，以及IPO使用步骤，帮助读者快速上手使用IPO。]]></description>
			<content:encoded><![CDATA[<p><strong>认识IPO</strong></p>
<p>IPO优化（全称Interprocedural Optimization）在很长一段时间内都是英特尔编译器的几大特色优化手段之一。在我刚接触英特尔编译器的时候，编译器版本号还是7.0，由于IPO易于使用而且优化效果出色，IPO就已经被英特尔编译器用户广泛使用了。</p>
<p>首先，我们来看一看IPO主要包含哪些优化：</p>
<ul type="disc">
<li>Inlining：将函数体插入到函数调用点，以减少函数调用带来的开销。但是并不是一见到函数调用就要inline，因为inline也是有一定开销的，inline后生成的代码变大了，过大的代码可能导致i-cache miss的问题。所以英特尔编译器会分析源代码，根据具体情况来决定是否要做inlining。Inlining的变种是Partial inlining，例如下面伪代码 </li>
</ul>
<blockquote>
<pre>void foo()
{
    some work here...
    if (...)
    {
        some other work here...
    }
}</pre>
</blockquote>
<p style="padding-left: 30px">在调用函数foo的时候，其前半部分肯定会被执行，但被if语句包围的部分就不一定会被执行到，甚至可能很少被执行到。Partial inlining的意思是既然我把整个函数体都inline的话代码可能过大，那我就把函数体前半部分代码给inline到调用点了，而后半部分不做inline。这样的结果就是，编译器生成一个函数叫foo_2</p>
<blockquote>
<pre>void foo_2()
{
    some other work here...
}</pre>
</blockquote>
<p style="padding-left: 30px">函数调用点就变成</p>
<blockquote>
<pre>some work here...
if (...)
    call foo_2();</pre>
</blockquote>
<p style="padding-left: 30px">这样生成的代码相对完全inline较小，同时还仍然能减少函数调用次数，提高程序运行速度。</p>
<ul type="disc">
<li>constant propagation：当我们把一个常量赋值给一个变量时，编译器通过常量扩散这种优化可以将该变量的使用替换为该常量。一般来说，常量扩散可以用在基本块内，也可以在更复杂的控制流上。IPO可以将常量扩散的使用范围跨越函数边界甚至跨越文件，也就是说，IPO可以将常量从一个函数到另一个函数。</li>
<li>alias analysis：别名分析，对其他优化，比如向量化、并行化，有非常大的帮助。因为向量化和并行化对循环有个基本的要求，循环的每次迭代（iteration）之间没有依赖关系（dependency），编译器需要对循环进行依赖性分析以确定循环是否是无依赖的。别名分析能帮助编译器更容易确定循环的依赖性。</li>
<li>dead function elimination：通过分析整个程序，发现未被调用过的函数，在生成的二进制代码中不包含这些函数。</li>
<li>whole program analysis：IPO能够跨文件做分析、优化，在后面关于如何使用IPO的介绍中，我们会看到英特尔编译器是如何做到跨文件分析和优化的。</li>
<li>array dimension padding：通过向数组内填充一些空字节，以减少cache组冲突（cache set conflict）来提高cache利用率，并且使数组访问能与cache line边界对齐来避免false sharing。关于false sharing的介绍，请看<a href="http://en.wikipedia.org/wiki/False_sharing">http://en.wikipedia.org/wiki/False_sharing</a>。</li>
<li>structure splitting and field reordering：将一个结构分解成两个或多个较小的结构，以便能让较小较热的结构尽可能留在cache中，获得更好的cache命中率。所谓较热，是指在某一段时间内被频繁访问。</li>
<li>C++ class hierarchy analysis：C++的重载使得C++代码非常灵活，而且比C代码更具可扩展性。然而，重载给编译器带来了一些小麻烦，在缺乏足够信息的情况下，编译器并不知道一个类的某个方法（method）最终是在哪个父类中定义的。C++类层次结构分析能解决这个问题，通过这个分析，编译器可以获得完整的类层次结构图，从而在编译时推断出定义该方法的类，以达到提高程序运行效率的目的。</li>
<li>Passing arguments in registers：函数调用时使用寄存器传递参数，以减少内存访问。</li>
</ul>
<p>在某些特定条件和范围内（比如在一个基本块中），其他优化器也做上述部分优化，但IPO能在更大范围内使用这些优化，因此优化的效果更加出色。</p>
<p>IPO分两种模式：单文件编译模式和多文件编译模式。</p>
<p>在单文件模式下（使用/Qip或-ip开关），编译器在单个文件的范围内使用以上优化，给每个源文件都生成一个对应的二进制文件，然后再将所有二进制文件链接起来。使用IPO单文件模式的步骤与使用普通优化步骤完全一样。</p>
<p>在多文件模式下（使用/Qipo或-ipo开关），编译器在编译过程中给每个源文件生成的不再是真正的二进制文件，而是"虚拟的二进制文件"。之所以叫虚拟的二进制文件是因为这些文件中存储的不是二进制代码，而是编译器生成的中间语言。这些中间语言文件的大小通常是真实二进制文件大小的数十倍，不需要觉得惊奇。编译器在编译过程中实际上并不做任何优化，只是将源代码转换成中间语言。那么在多文件模式下，优化是在什么时候进行的呢？是在链接的时候进行的。请看以下IPO示意图：</p>
<p style="text-align: center"><strong><img class="aligncenter" src="http://software.intel.com/file/29666" alt="" /></strong> </p>
<p>因为在编译过程中生成的是中间语言文件，系统的链接器是无法识别的，所以我们在链接时不是使用link或ld，而应该使用icl、icc或icpc来链接，也可以使用英特尔编译器的xilink或xild。无论用哪一个来做链接，他们首先会将所有中间语言文件合并到一起，这个合并后的巨大的中间语言文件包含了所有源代码信息，这样英特尔编译器就能跨越文件对所有源代码（合并后的中间语言文件）进行集中的分析和优化了，然后再将优化后生成的真正的二进制文件传递给真正的链接器link或ld进行链接，最后得到可执行文件。因为编译器能够一次性看到所有源代码的信息，所以它做的分析具有更高的确定性和准确性，能够实施的优化也要多得多。</p>
<p>下面让我们来看一下如何使用IPO。 </p>
<p><strong> </strong></p>
<p><strong>使用IPO</strong></p>
<p>IPO的使用并不复杂，只需要加上/Qipo或-ipo开关，例如：</p>
<table style="height: 86px" border="1" cellspacing="0" cellpadding="0" width="483">
<thead>
<tr>
<td width="34%" valign="top"><strong>Operating System </strong></td>
<td width="65%" valign="top"><strong>Example Command</strong></td>
</tr>
</thead>
<tbody>
<tr>
<td width="34%" valign="top">Linux and Mac OS X</td>
<td width="65%" valign="top">icpc -ipo -o app a.cpp b.cpp c.cpp</td>
</tr>
<tr>
<td width="34%" valign="top">Windows</td>
<td width="65%" valign="top">icl /Qipo /Feapp.exe a.cpp b.cpp c.cpp</td>
</tr>
</tbody>
</table>
<p>以上命令行将编译这三个源文件并生成可执行文件app和app.exe。</p>
<p>有时候我们需要将编译和链接分开来做。</p>
<p>首先，加上/Qipo或-ipo开关编译源代码：</p>
<table style="height: 86px" border="1" cellspacing="0" cellpadding="0" width="481">
<thead>
<tr>
<td style="text-align: left" width="34%" valign="top"><strong>Operating System </strong></td>
<td style="text-align: left" width="65%" valign="top"><strong>Example Command</strong></td>
</tr>
</thead>
<tbody>
<tr>
<td width="34%" valign="top">Linux and Mac OS X</td>
<td width="65%" valign="top">icpc -ipo -c a.cpp b.cpp c.cpp</td>
</tr>
<tr>
<td width="34%" valign="top">Windows*</td>
<td style="text-align: left" width="65%" valign="top">icl /Qipo /c a.cpp b.cpp c.cpp</td>
</tr>
</tbody>
</table>
<p>然后，我们将生成的二进制文件链接起来。</p>
<table style="height: 86px" border="1" cellspacing="0" cellpadding="0" width="480">
<thead>
<tr>
<td width="34%" valign="top"><strong>Operating System </strong></td>
<td width="65%" valign="top"><strong>Example Command</strong></td>
</tr>
</thead>
<tbody>
<tr>
<td width="34%" valign="top">Linux and Mac OS X</td>
<td width="65%" valign="top">icpc -o app a.o b.o c.o</td>
</tr>
<tr>
<td width="34%" valign="top">Windows</td>
<td width="65%" valign="top">icl /Feapp.exe a.obj b.obj c.obj</td>
</tr>
</tbody>
</table>
<p>看上去跟我们不使用IPO时的步骤和命令行差不多。需要注意的是，我们这里使用的是icpc（如果是C代码则使用icc）和icl作为链接器来进行链接。其实也可以使用xild（Linux和Mac OS X）和xilink（Windows）来链接，但是不能直接使用ld和link来链接。</p>
<p>类似的，当您需要创建.lib文件或archive文件时，请使用xilib和xiar替代lib和ar。</p>
<p>如果是在Microsoft* Visual Studio*的IDE内使用英特尔编译器，英特尔编译器安装程序已经自动为您配置好xilink和xilib了，要使用IPO只需要打开/Qipo开关即可。</p>
<p>在下一篇文章《<a title="永久链接： 英特尔编译器优化系列：深入IPO优化" rel="bookmark" href="http://software.intel.com/zh-cn/blogs/2010/08/16/ipo-2/">英特尔编译器优化系列：深入IPO优化</a>》中，我们会深入IPO，介绍一些IPO的高级技巧。</p>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/zh-cn/blogs/2010/08/13/ipo/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Intel® Cilk Plus程序中使用锁需要注意的问题</title>
		<link>http://software.intel.com/zh-cn/blogs/2010/06/27/intel-cilk-plus/</link>
		<comments>http://software.intel.com/zh-cn/blogs/2010/06/27/intel-cilk-plus/#comments</comments>
		<pubDate>Sat, 26 Jun 2010 16:24:34 +0000</pubDate>
		<dc:creator>Feilong H. (Intel)</dc:creator>
				<category><![CDATA[并行计算]]></category>

		<guid isPermaLink="false">http://software.intel.com/zh-cn/blogs/2010/06/27/intel-cilk-plus/</guid>
		<description><![CDATA[在Intel® Cilk Plus程序中可以使用多种锁，包括操作系统提供的各种同步对象，例如Windows*的临界区和Linux*的互斥锁，还可以使用Intel® Threading Building Blocks库提供的tbb::mutex，还有编译器支持的具有原子性的基本函数（Intrinsics）。 在Intel® Cilk Plus程序中使用锁与在其他程序中使用锁没有太大的区别。唯一需要注意的是，在Intel® Cilk Plus程序中要避免锁跨越strand边界。也就是说，同辈的strand可以使用同一个锁，但是父strand和子strand共享一个锁存在潜在的问题，容易导致死锁。我们通过一段代码来看一下跨越strand边界的锁会引发的问题： int child(tbb::mutex &#38;m, int &#38;a) {     m.lock();     a++;     m.unlock(); } int parent(int a, int b, int c) {     cilk::mutex m;     try     {         cilk_spawn child(&#38;m, a);         m.lock();         throw a;     }     catch(...)     {         m.unlock();     } } 我们知道，在包含cilk_spawn的try语句块后面有一个隐含的cilk_sync（关于Intel® Cilk Plus的异常处理，请参阅Intel® Cilk Plus用户手册6.4章节）。正因为这cilk_sync，一旦在try语句块内发生异常，程序只有等到所有的子strand完成后才能继续执行cilk_sync后面的代码。 在上面的代码片段中我们可以看到，父strand和子strand共享着同一把锁m。如果父strand在子strand之前获得了这把锁，随后throw a语句抛出了异常，因为子strand已经无法获得该锁，也就无法完成执行，最终导致无法执行到catch语句块，这样，程序就死锁了。 如果一个函数拥有一把锁，而且子strand会去获取这个锁，那么只要这个函数在释放这把锁之前不抛出任何异常，这个程序就不会发生死锁。但是，要避免抛出异常不是很简单的事情。因此我们建议您遵守以下规则： 父strand与子strand不要使用同一把锁。也就是说，锁住同辈strand，但不要锁住子strand。 如果您必须锁住子strand，那么请把获取锁、做一些工作、释放锁这些代码放到一个单独的函数中，然后在子strand中调用这个函数。 [...]]]></description>
			<content:encoded><![CDATA[<p>在Intel® Cilk Plus程序中可以使用多种锁，包括操作系统提供的各种同步对象，例如Windows*的临界区和Linux*的互斥锁，还可以使用Intel® Threading Building Blocks库提供的tbb::mutex，还有编译器支持的具有原子性的基本函数（Intrinsics）。</p>
<p>在Intel® Cilk Plus程序中使用锁与在其他程序中使用锁没有太大的区别。唯一需要注意的是，在Intel® Cilk Plus程序中要避免锁跨越strand边界。也就是说，同辈的strand可以使用同一个锁，但是父strand和子strand共享一个锁存在潜在的问题，容易导致死锁。我们通过一段代码来看一下跨越strand边界的锁会引发的问题：</p>
<blockquote><p>int child(tbb::mutex &amp;m, int &amp;a)</p>
<p>{</p>
<p>    m.lock();</p>
<p>    a++;</p>
<p>    m.unlock();</p>
<p>}</p>
<p>int parent(int a, int b, int c)</p>
<p>{</p>
<p>    cilk::mutex m;</p>
<p>    try</p>
<p>    {</p>
<p>        cilk_spawn child(&amp;m, a);</p>
<p>        m.lock();</p>
<p>        throw a;</p>
<p>    }</p>
<p>    catch(...)</p>
<p>    {</p>
<p>        m.unlock();</p>
<p>    }</p>
<p>}</p></blockquote>
<p>我们知道，在包含cilk_spawn的try语句块后面有一个隐含的cilk_sync（关于Intel® Cilk Plus的异常处理，请参阅Intel® Cilk Plus用户手册6.4章节）。正因为这cilk_sync，一旦在try语句块内发生异常，程序只有等到所有的子strand完成后才能继续执行cilk_sync后面的代码。</p>
<p>在上面的代码片段中我们可以看到，父strand和子strand共享着同一把锁m。如果父strand在子strand之前获得了这把锁，随后throw a语句抛出了异常，因为子strand已经无法获得该锁，也就无法完成执行，最终导致无法执行到catch语句块，这样，程序就死锁了。</p>
<p>如果一个函数拥有一把锁，而且子strand会去获取这个锁，那么只要这个函数在释放这把锁之前不抛出任何异常，这个程序就不会发生死锁。但是，要避免抛出异常不是很简单的事情。因此我们建议您遵守以下规则：</p>
<ul>
<li>父strand与子strand不要使用同一把锁。也就是说，锁住同辈strand，但不要锁住子strand。</li>
<li>如果您必须锁住子strand，那么请把获取锁、做一些工作、释放锁这些代码放到一个单独的函数中，然后在子strand中调用这个函数。</li>
<li>如果一个父strand需要获取一个锁、给一些变量赋值、然后释放锁，在它拥有这个锁的时候没有try语句块、可能抛异常的函数调用（包括重载运算符）、衍生和同步的情况下，这一系列操作是安全的。请记住，在获取锁之前先将要赋的值计算好。</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://software.intel.com/zh-cn/blogs/2010/06/27/intel-cilk-plus/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

