引言

随着CPU多核的普及,编程时充分利用这个特性越显重要。本文首先用传统的嵌套循环进行数组填充,然后用.NET 4.0中的System.Threading.Tasks提供的Parallel Class来并行地进行填充(当然这里也用到嵌套循环),通过对比发现其中差异。主要内容如下:

  1. 通常的数组填充
  2. 并行的组数填充
  3. 性能比较
  4. System.Threading.Tasks分析,这个将在续篇.NET(C#) Internals: 以一个数组填充的例子初步了解.NET 4.0中的并行(二)中介绍

1、通常的数组填充

首先看如下代码:

  1. using System;  
  2.  
  3. namespace ParallelForSample  
  4. {  
  5.   public class SingleCore  
  6.   {  
  7.     public static void Calculate(int calcVal)  
  8.     {  
  9.       Utility util = new Utility();  
  10.       util.Start();  
  11.  
  12.       int[,] G = new int[calcVal, calcVal];  
  13.       for (int k = 0; k < calcVal; k++)  
  14.         for (int i = 0; i < calcVal; i++)  
  15.           for (int j = 0; j < calcVal; j++)  
  16.             G[i, j] = Math.Min(G[i, j], G[i, k] + G[k, j]);  
  17.       util.Stop();  
  18.  
  19.     }  
  20.   }  

上面的粗体红色显示的几行代码就是实现数组填充,这个很好理解不用多费口舌。补充说明的是:上面的Utility是为了统计性能而编写的一个类,它主要就是用到了Stopwatch对象——它提供一组方法和属性,可用于准确地测量运行时间。Utility的代码如下:

  1. public class Utility  
  2.     {  
  3.         private Stopwatch _stopwatch;  
  4.         public void Start()  
  5.         {  
  6.             _stopwatch = new Stopwatch();  
  7.             _stopwatch.Start();  
  8.         }  
  9.  
  10.         public void Stop()  
  11.         {  
  12.             _stopwatch.Stop();  
  13.             TimeSpan ts = _stopwatch.Elapsed;  
  14.             string elapsedTime = String.Format("{0:00}:{1:00}:{2:00}.{3:00}",  
  15.                 ts.Hours, ts.Minutes, ts.Seconds,  
  16.                 ts.Milliseconds / 10);  
  17.             Console.WriteLine("Time taken : {0}", elapsedTime);  
  18.         }  
  19.     } 

利用它我们就可以对数组填充所耗费的时间进行计算了。

2、并行的组数填充

为了充分利用CPU的多核,我们编写如下代码:

  1. using System;  
  2. using System.Threading.Tasks;  
  3.  
  4. namespace ParallelForSample  
  5. {  
  6.   public class MultiCore  
  7.   {  
  8.     public static void Calculate(int calcVal)  
  9.     {  
  10.       Utility util = new Utility();  
  11.       util.Start();  
  12.  
  13.       int[,] G = new int[calcVal, calcVal];  
  14.  
  15.       Parallel.For(0, calcVal,  
  16.         delegate(int k)  
  17.         {  
  18.           Parallel.For(0, calcVal, delegate(int i)  
  19.           {  
  20.             for (int j = 0; j < calcVal; j++)  
  21.               G[i, j] = Math.Min(G[i, j], G[i, k] + G[k, j]);  
  22.           });  
  23.         }  
  24.       );  
  25.  
  26.       util.Stop();  
  27.     }  
  28.   }  

留意上面的红色粗体显示的几行代码,它利用了Parallel.For Method (Int32, Int32, Action<Int32>)方法,Parallel类位于命名空间System.Threading.Tasks中,它支持并行循环。此Parallel.For方法使得它里面的迭代可能并行地运行,注意到上述代码中它的第三个参数是一个委托。在(0,calcVal)之间,这个委托将被调用。

3、性能比较

现在我们来测试一下,上面两种方法的执行性能差异如何,下载源码。其实,核心代码已经在上面贴出来了,现在注意是编写实例来测试,代码主要如下:

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5.  
  6. namespace ParallelForSample  
  7. {  
  8.     class Program  
  9.     {  
  10.         static void Main(string[] args)  
  11.         {  
  12.             Console.WriteLine("Single core");  
  13.             SingleCore.Calculate(1000);  
  14.             Console.WriteLine("Multi core");  
  15.             MultiCore.Calculate(1000);  
  16.             Console.WriteLine("Finished");  
  17.             Console.ReadKey();  
  18.         }  
  19.     }  

运行之后得到如下结果:(不同电脑配置不同,得出结果不同)

以一个数组填充的例子初步了解.NET 4.0中的并行(一)

图1、性能比较

从结果可以看出,并行的数组填充比通常的数组填充性能更高。

  1. System.Threading.Tasks分析,这个将在续篇.NET(C#) Internals: 以一个数组填充的例子初步了解.NET 4.0中的并行(二)中介绍……