PLINQ性能不佳

时间:2012-06-06 14:23:37

标签: c# 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 Opteron(tm)。

相同的结果并行查询比连续查询运行慢。 你能告诉我我的问题是什么吗?

感谢。

3 个答案:

答案 0 :(得分:5)

我想这与一些开销有关。 你迭代的集合非常小(32k短裤),对这些项目执行的操作非常简单。

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

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

答案 1 :(得分:3)

您的“问题”在没有意义时使用PLINQ

PLINQ并不总是更快。 PLINQ总是会增加开销。

就CPU指令而言;无论你需要做多少工作(称之为X),你最终都会执行超过X指令。 PLINQ将开展大量额外的工作,开始执行线程,将工作委派给他们并将结果恢复为可以使用的表单。

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

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

  

顺序执行2毫秒

     

并行执行40毫秒

我还可以看到PLINQ代码创建的八个工作线程。这8个线程代表了2毫秒计算量的大量开销。通过两次运行Parallel Execution基准测试,您可以了解它的开销是多少。工作线程会徘徊。这是第二次运行它的输出:

  

顺序执行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;
}

答案 2 :(得分: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,它在那里效果更好。我没有意识到maxvalue大约是30k,所以评论可能不适用于你的情况。可能问题是AsParralel错失了地方。