2012-06-06 45 views
3

我试图实现PLINQ的例子,但面临以下问题 我的顺序查询比并行查询执行得更快。PLINQ性能不佳

这里是代码示例:

 Stopwatch sw = new Stopwatch(); 

     int[] vals = Enumerable.Range(0, Int16.MaxValue).ToArray(); 

     sw.Start(); 
     int[] x1 = vals.Where(x => x % 2 == 0).ToArray(); 
     sw.Stop(); 
     Console.WriteLine("Sequential Execution {0} milliseconds", sw.ElapsedMilliseconds); 


     sw.Restart(); 
     int[] x2 = vals.AsParallel().Where(x => x % 2 == 0).ToArray(); 
     sw.Stop(); 
     Console.WriteLine("Parallel Execution {0} milliseconds", sw.ElapsedMilliseconds); 

我的机器是奔腾(R)双 - 核心 我也试过在四 - 核AMD皓龙(TM)。

相同的结果并行查询比顺序运行速度慢。 你能告诉我什么是我的问题?

谢谢。

+1

http://stackoverflow.com/questions/7582591/how-to-plinq-an-existing-linq-query-with-加入(阅读我的推荐读物)。简而言之:你的模操作太微不足道了。你需要更复杂的操作。 –

回答

2

这一次似乎更好地工作:

 Stopwatch sw = new Stopwatch(); 

     int[] vals = Enumerable.Range(0, 10000000).ToArray(); 

     sw.Start(); 
     var x1 = vals.Where(x => x % 2 == 0).ToList(); 
     sw.Stop(); 
     Console.WriteLine("Sequential Execution {0} milliseconds", sw.ElapsedMilliseconds); 


     sw.Restart(); 
     var x2 = vals.Where(x => x % 2 == 0).AsParallel().ToList(); 
     sw.Stop(); 
     Console.WriteLine("Parallel Execution {0} milliseconds", sw.ElapsedMilliseconds); 

不启动另一个线程200倍的值。它需要更多的启动/唤醒其他线程,而不是在单个线程上完成整个循环。 +更多线程表示线程同步机制。 LE:好吧,我尝试了Int16.MaxValue,它在那里效果更好。我不知道最大值约为30k,因此评论可能不适用于您的案例。可能问题在于AsParralel错位。

+0

我刚刚尝试了一些书籍的例子,我知道将集合分解为少量数据的线程会比较耗时并行执行,但我没有意识到32K值是不够的。 –

5

我想这与一些开销有关。 您迭代的集合非常小(32k短裤),并且对这些项目执行的操作是微不足道的。

在这种情况下,集合的划分,过滤和重新合并可能比在单个迭代中执行集合要昂贵得多。

如果您的比较成本较高(例如搜索字符串)并且您的收藏增长,您会看到结果发生变化。

+0

是的,PLINQ不是一个能够加快每个查询速度的银弹。当处理每个项目时会有延迟,比如从数据库加载它时会发光。由于你的查询没有延迟,并且你无论如何都会最大限度地利用CPU,所以PLINQ的开销会降低你的速度。 –

3

你的“问题”是使用PLINQ时,它没有任何意义

PLINQ不会总是更快。 PLINQ将永远增加开销。

就CPU指令而言;无论您需要做多少工作(称之为X),您最终都会执行超过X条指令。 PLINQ将开展大量额外的工作,将工作委托给他们,并将结果返回到您可以使用的表单中。

这样做的好处是你可以有多个CPU/Core的工作。有时它会更快。当你所做的CPU工作量相对于开销很小时,它会变慢。

当我运行代码,我得到以下的输出:

顺序执行2毫秒

并行执行40毫秒

我还可以看到正在创建工作线程由PLINQ代码。这8个线程代表了2毫秒计算量的大量开销。通过两次运行“并行执行”基准测试,您可以感觉到它有多少开销。工作线程将会四处流动。这是我的输出与运行它的第二时间:

顺序执行2毫秒

并行#1执行40毫秒

并行#2执行3毫秒

第二时间是快多了;但仍然比不做任何事情慢。因为即使已经创建了工作线程 - PLINQ仍然需要做工作来分割线程之间的操作并以可以访问的格式重新获得结果。

您需要做的工作越多,对开销的影响就越小。在这个例子中,我用一个名为IsValid的静态函数替换了你的Where lambda,并且我计算了%2 500次而不是一次。

static bool IsValid(int input) 
{ 
    int result=0; 

    for(int i =0;i<500;i++)    
     result = input%2; 

    return result == 0; 
} 

现在 - 我的执行时间是:

顺序执行36毫秒

并行#1执行47毫秒

并行#2执行9毫秒

你可以看到PLINQ仍然很慢第一次执行 - 但在第二次执行时速度明显加快。如果通过将环路从500增加到5000(在我的机器上)来实现CPU工作,PLINQ将胜出,而不用担心。

TL; DR - 你做得对;你只是没有做足够的工作来让PLINQ成为更快的选择。

下面是我做了什么,整个源代码:

static void Main(string[] args) 
{ 
    Stopwatch sw = new Stopwatch(); 

    int[] vals = Enumerable.Range(0, Int16.MaxValue).ToArray(); 

    sw.Start(); 
    int[] x1 = vals.Where(IsValid).ToArray(); 
    sw.Stop(); 
    Console.WriteLine("Sequential Execution {0} milliseconds", sw.ElapsedMilliseconds); 

    sw.Restart(); 
    int[] x2 = vals.AsParallel().Where(IsValid).ToArray(); 
    sw.Stop(); 
    Console.WriteLine("Parallel #1 Execution {0} milliseconds", sw.ElapsedMilliseconds); 

    sw.Restart(); 
    int[] x3 = vals.AsParallel().Where(IsValid).ToArray(); 
    sw.Stop(); 
    Console.WriteLine("Parallel #2 Execution {0} milliseconds", sw.ElapsedMilliseconds); 

    Console.Read(); 
} 

static bool IsValid(int input) 
{ 
    int result=0; 

    for(int i =0;i<5000;i++)    
     result = input%2; 

    return result == 0; 
} 
+0

您确定第二个并行查询执行速度更快吗?因为线程已经启动了吗?如果你试图执行相同的顺序查询两次,它也更快,我只是认为这是查询缓存 – MaRuf