DotNet并行计算的使用误区二

开发 后端
.NET是一个微软开发的编程环境,里面可以使用C#,VB等多种编程语言。本文主要介绍.NET中,也就是DOTNET中的并行计算。让我们一起来看。

并行计算或称平行计算是相对于串行计算来说的。所谓并行计算可分为时间上的并行和空间上的并行。 时间上的并行就是指流水线技术,而空间上的并行则是指用多个处理器并发的执行计算。

上接 DotNet并行计算的使用误区一

误区三 . 并行计算是运行时的事

的确,DotNet会在运行时决定是否使用并行库处理代码,但是早在你编译代码时,编译器就早已为这一时刻做好准备,换就话说:

1. 使用并行库处理代码与普通方式对比,IL的结构是不同的。

2. 即使你选择使用并行计算,并且你也确实拥有多核(线程)CPU,运行时你的代码也不一定是并行的。

使用TPL后CLR可能会分解任务,这一依据的其中之一是由IL支持的,IL将并行的任务代码分离,以便在将来的操作中并行,这一点可以从以下的示例中看出来,以下两段示例的核心C#代码都是Tostring()和Sleep(),Code A使用For包含Sleep,Code B使用Parallel.For处理:

Code Part A:

IL:

  1. IL_000e: callvirt instance void [System]System.Diagnostics.Stopwatch::Start()   
  2. IL_0013: nop   
  3. IL_0014: ldc.i4.0   
  4. IL_0015: stloc.2   
  5. IL_0016: br.s IL_0031   
  6. IL_0018: nop   
  7. IL_0019: ldloca.s i   
  8. IL_001b: call instance string [mscorlib]System.Int32::ToString()   
  9. IL_0020: stloc.0   
  10. IL_0021: ldc.i4 0xc8   
  11. IL_0026: call void [mscorlib]System.Threading.Thread::Sleep(int32)   
  12. IL_002b: nop   
  13. IL_002c: nop   
  14. IL_002d: ldloc.2   
  15. IL_002e: ldc.i4.1   
  16. IL_002f: add   
  17. IL_0030: stloc.2   
  18. IL_0031: ldloc.2   
  19. IL_0032: ldc.i4.s 10   
  20. IL_0034: clt   
  21. IL_0036: stloc.3   
  22. IL_0037: ldloc.3   
  23. IL_0038: brtrue.s IL_0018   
  24. IL_003a: ldloc.1   
  25. IL_003b: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop()   

我们注意到,Code Part A的Sleep是直接出现在Load方法中的。

 

再来看看Parallel方式:

Code Part B:

Form1_Load:

 

  1. IL_0019: callvirt instance void [System]System.Diagnostics.Stopwatch::Start()   
  2. IL_001e: nop   
  3. IL_001f: ldc.i4.0   
  4. IL_0020: ldc.i4.s 10   
  5. IL_0022: ldloc.1   
  6. IL_0023: ldftn instance void WindowsFormsApplication4.Form1/'<>c__DisplayClass1'::'<Form1_Load>b__0'(int32)   
  7. IL_0029: newobj instance void class [mscorlib]System.Action`1<int32>::.ctor(object, native int) IL_002e: call valuetype [mscorlib]System.Threading.Tasks.ParallelLoopResult [mscorlib]System.Threading.Tasks.Parallel::For(int32, int32, class [mscorlib]System.Action`1<int32>)   
  8. IL_0033: pop   
  9. IL_0034: ldloc.0   
  10. IL_0035: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop()   
  11. //注意,Sleep已经不在Load方法中了,而是被一个“b__0”代替,并行代码与宿主代码分离,以下就是b__0的  
  12. IL: .method public hidebysig instance void '<Form1_Load>b__0'(int32 i) cil managed   
  13. {   
  14. // 代码大小 26 (0x1a)   
  15. .maxstack 8   
  16. IL_0000: nop   
  17. IL_0001: ldarg.0   
  18. IL_0002: ldarga.s i   
  19. IL_0004: call instance string [mscorlib]System.Int32::ToString()   
  20. IL_0009: stfld string WindowsFormsApplication4.Form1/'<>c__DisplayClass1'::a IL_000e: ldc.i4 0xc8   
  21. IL_0013: call void [mscorlib]System.Threading.Thread::Sleep(int32)   
  22. IL_0018: nop   
  23. IL_0019: ret   
  24. // end of method '<>c__DisplayClass1'::'<Form1_Load>b__0'   
  25.  

 

结构图:

 

以上的红色代码就是在Code A中出现的主要代码。再让我们重温一下这张图,IL的代码任务已经很明显的指示了出来。

 

每当我们增加一个并行代码段,IL中就会增加一个b_N块:假如我们的代码中包含两个Parallel块,每块的主代码与上述一致,IL如下:

 

  1. IL_0019: callvirt instance void [System]System.Diagnostics.Stopwatch::Start()   
  2. IL_001e: nop   
  3. IL_001f: ldc.i4.0   
  4. IL_0020: ldc.i4.s 10   
  5. IL_0022: ldloc.1 IL_0023: ldftn instance void WindowsFormsApplication4.Form1/'<>c__DisplayClass2'::'<Form1_Load>b__0'(int32)   
  6. IL_0029: newobj instance void class [mscorlib]System.Action`1<int32>::.ctor(object, native int) IL_002e: call valuetype [mscorlib]System.Threading.Tasks.ParallelLoopResult [mscorlib]System.Threading.Tasks.Parallel::For(int32, int32, class [mscorlib]System.Action`1<int32>)   
  7. IL_0033: pop  
  8. IL_0034: ldc.i4.0   
  9. IL_0035: ldc.i4.s 10   
  10. IL_0037: ldloc.1   
  11. IL_0038: ldftn instance void WindowsFormsApplication4.Form1/'<>c__DisplayClass2'::'<Form1_Load>b__1'(int32)   
  12. IL_003e: newobj instance void class [mscorlib]System.Action`1<int32>::.ctor(object, native int) IL_0043: call valuetype [mscorlib]System.Threading.Tasks.ParallelLoopResult [mscorlib]System.Threading.Tasks.Parallel::For(int32, int32, class [mscorlib]System.Action`1<int32>)   
  13. IL_0048: pop   
  14. IL_0049: ldloc.0   
  15. IL_004a: callvirt instance void [System]System.Diagnostics.Stopwatch::Stop()   
  16.  

 

下图中会有对应模块出现:

 

上面的例子说明,在IL阶段已经为运行时的并行执行任务做了准备,编译阶段将并行任务从宿主中分离出来,运行阶段决定是否采用并行方式执行任务。

【编辑推荐】

  1. 3.9.3 云计算与并行计算
  2. 1.4 商业应用的并行计算
  3. 1.3 网格计算:分布式并行计算
  4. 并行计算的难点与数学原理解析

 

责任编辑:于铁 来源: 博客园
相关推荐

2011-04-20 17:15:21

并行计算

2019-04-18 09:15:05

DaskPython计算

2010-03-22 14:45:40

云计算

2014-04-24 10:25:15

2021-06-01 05:51:37

云计算并行计算分布式计算

2014-01-21 11:16:59

MPI并行计算

2012-08-17 09:32:52

Python

2010-03-19 17:23:45

云计算

2009-12-18 09:38:27

.NET 4.0并行计

2023-10-30 08:57:19

.Net开发并行计算

2010-06-11 08:52:17

并行计算

2011-08-29 10:40:02

浪潮英特尔并行计算

2010-06-10 08:37:04

并行计算

2017-04-24 12:07:44

Spark大数据并行计算

2010-04-21 09:23:09

.NET 4

2010-03-11 15:23:44

Visual Stud

2012-12-18 15:33:44

递归数据并行计算

2016-10-25 16:38:32

天云软件云计算张福波

2009-10-14 20:22:25

2022-07-08 10:59:32

深度学习计算
点赞
收藏

51CTO技术栈公众号