C#4.0入門 第二章 工作平行程式庫—第三頁 效能的差異

來源:互聯網
上載者:User
 
這裡有一張單色(2值)的PNG的圖片(1024*1024)。因為是單色,所以只有黑跟白兩個值(實際上使用了只有黑跟白兩色的8位PNG格式的圖片檔案)。到底,哪些像素是黑色的,我們編寫一個程式來查點一下。同時用它來進行單任務和2~4任務分別進行查點時效能的比較。

比較單任務與多任務

 using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
class Program
{
public static IEnumerable<Color> Pixels(int from, int to)
{
var bm = new Bitmap(@"s:\sample001.png");
for (int y = from; y < to; y++)
{
for (int x = 0; x < bm.Width; x++)
{
yield return bm.GetPixel(x, y);
}
}
}
private static bool isBlack(Color c)
{
return c.B == 0 && c.G == 0 && c.R == 0;
}
private static void count(int fromInclusive, int toExclusive, ref int count, object lockTarget)
{
var q = from n in Pixels(fromInclusive, toExclusive)
where isBlack(n)
select n;
int r = q.Count();
lock (lockTarget) { count += r; }
}
static void Main(string[] args)
{
Object thisLock = new Object();
DateTime start1 = DateTime.Now;
int count1 = 0;
count(0, 1024, ref count1, thisLock);
Console.WriteLine(DateTime.Now - start1);
Console.WriteLine("{0} Black Pixels by single task", count1);
DateTime start2 = DateTime.Now;
int count2 = 0;
Parallel.Invoke(() => count(0, 512, ref count2, thisLock),
() =>count(512, 1024, ref count2, thisLock));
Console.WriteLine(DateTime.Now - start2);
Console.WriteLine("{0} Black Pixels by 2 task", count2);
DateTime start3 = DateTime.Now;
int count3 = 0;
Parallel.Invoke(() =>count(0, 342, ref count3, thisLock),
() => count(342, 682, ref count3, thisLock),
() => count(682, 1024, ref count3, thisLock));
Console.WriteLine(DateTime.Now - start3);
Console.WriteLine("{0} Black Pixels by 3 task", count3);
DateTime start4 = DateTime.Now;
int count4 = 0;
Parallel.Invoke(() =>count(0, 256, ref count4, thisLock),
() => count(256, 512, ref count4, thisLock),
() => count(512, 768, ref count4, thisLock),
() => count(768, 1024, ref count4, thisLock));
Console.WriteLine(DateTime.Now - start4);
Console.WriteLine("{0} Black Pixels by 4 task", count4);
}
}


這段代碼乍看上去有點冗長,其實是必要的,正式開發中存在著大量的這種代碼。首先,在平行處理的每一個任務中分別把畫像檔案一一讀入。4任務的情況下要讀4次相同的檔案。雖然乍看上去是多餘的,但是因為Bitmap類是非安全執行緒的,不能對別的線程進行訪問。因此,儘管多餘,還是要在一個處理線程內讀入相同檔案。


請注意使用了lock語句。在這種程式裡,要防止互相競爭訪問而導致死結的發生。


另外,下面的代碼中,變數r是可以去掉的。

未去掉變數r的代碼

 int r = q.Count();
lock (lockTarget) { count += r; }


可以象下例那樣去掉變數r。

去掉變數r的代碼

 lock (lockTarget) { count += q.Count(); }


但是,查點像素的處理,實際上是在Count方法內進行的。因此,如果這樣進行改寫的話,這個處理本身就變成被排他性的鎖住了,全體處理就被延遲了,從而失去了有效利用多核進行平行處理的意義。


還有一點要注意的是,如果任務數超過了實際存在的核心數,反而會影響效能。以下為在雙核CPU上執行的例子。

運行結果1(在雙核CPU上啟動並執行例子。所花的時間,2任務<3任務=4任務<單任務)

 00:00:07.9630000
16470 Black Pixels by single task
00:00:03.0170000
16470 Black Pixels by 2 task
00:00:03.0870000
16470 Black Pixels by 3 task
00:00:03.0850000
16470 Black Pixels by 4 task


也就是說,如果在同一個核心上分配太多任務的話,反而會對效能產生影響。因此,在原本是雙核心,但是由於使用了HT技術,所以看起來好像是4核心的Core
i5上,運行結果又不一樣了。

運行結果2(在Core i5上啟動並執行例子。所花的時間,4任務<3任務<2任務<單任務)

 00:00:02.8451627
16470 Black Pixels by single task
00:00:01.4070805
16470 Black Pixels by 2 task
00:00:01.1200640
16470 Black Pixels by 3 task
00:00:01.0490600
16470 Black Pixels by 4 task


這種情況下,把分配的任務增加到4任務的時候,任務越多所花時間越短。但是,由於實際上是雙核心,所以從單任務變為2任務的時候,提速效果特別明顯,但是超過了2任務,就沒有這麼明顯的效果了。

■ 總結

把本章內容進行總結。

● 多核心時代已經開始。
● 軟體開發的多核心時代是從Visual Studio 2010/C#4.0/.NET
Framework4.0開始的。

● 利用Parallel.Invoke類,可以使並行變得很容易實現。

● 利用Parallel.ForEach方法實現foreach語句。

利用Parallel.For方法實現for語句。

● 但是,不能利用並行替換所有迴圈操作。

利用並行可以大幅度提高效能。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.