普林克性能不佳

本文关键字:性能 林克 | 更新日期: 2023-09-27 18:36:26

我正在尝试实现 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);

我的机器是奔腾® 双核 - 核心我也尝试过Quad - Core AMD Opteron(tm)。

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

谢谢。

普林克性能不佳

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

在这种情况下,集合、筛选和重新出现的分区可能比在单个迭代中执行相同的操作要昂贵得多。

如果您的比较费用更高(例如搜索字符串)并且您的收藏量增加,您将看到结果发生变化。

您的"问题"是使用 PLINQ 当它没有意义时

PLINQ不会总是更快。普林克将始终增加开销。

就CPU指令而言;无论你需要做多少工作(称之为X),你最终都会执行超过X条指令。 PLINQ 将做很多额外的工作来启动线程,将工作委派给他们,并将结果恢复为您可以使用的形式。

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

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

顺序执行 2 毫秒

并行执行 40 毫秒

我还可以看到 PLINQ 代码正在创建八个工作线程。 这八个线程代表了 2 毫秒计算的大量开销。 您可以通过运行并行执行基准测试两次来了解它的开销。 工作线程将闲逛。 这是我第二次运行它的输出:

顺序执行 2 毫秒

并行 #1 执行 40 毫秒

并行 #2 执行 3 毫秒

第二次要快得多;但仍然比什么都不做慢。 因为,即使已经创建了工作线程,PLINQ 仍然需要做一些工作来划分线程之间的操作,并以您可以访问的格式返回结果。

您必须完成的工作越多,开销的影响就越小。 在此示例中,我已将 Where lambda 替换为名为 IsValid 的静态函数,并且我计算 %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 获胜,毫无疑问。

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;
}

这个似乎效果更好:

        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被放错了位置。