1

以下代码在大约 2.5 秒内运行:

static void Main(string[] args)
{
    var service = new Service();
    Parallel.For(0, 100, i => {
        dynamic user = new ExpandoObject();
        user.data = new ExpandoObject();
        user.data.id = i;
        user.data.name = "User Name";
        var parsed = service.Parse(user);
    });
}

public class Service
{
    public User Parse(dynamic dynamicUser)
    {
        if (dynamicUser.data != null)
        {
            return new User
            {
                Id = dynamicUser.data.id,
                Name = dynamicUser.data.name
            };
        }
        return null;
    }
}

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}

但是,如果我将 Parallel.For() 循环更改为简单的 For 循环,它将在大约 200 毫秒内运行:

for (var i = 0; i < 100; i++)

所以我的问题是,为什么并行运行会慢得多?

我的理论是,在解析每个线程执行一次的动态对象时会有一些开销。在简单循环中,DLR 第一次做它的事情,然后不需要为每个后续调用做。

但同时,DLR 的开销发生在每次调用中。

这是一个正确的假设,还是我离谱?

4

2 回答 2

3

我怀疑你被你的诊断误导了。特别是,如果运行一个循环100次需要 2.5 秒,那真的非常非常慢。这是在调试器下吗?

这是我的盒子上的代码编译结果/o+,然后在控制台中运行。请注意,我在每个测试中运行 1,000,000 次循环迭代。

Void ExecuteParallel(): 00:00:00.7311773
Void ExecuteSerial(): 00:00:02.0514120
Void ExecuteParallel(): 00:00:00.6897816
Void ExecuteSerial(): 00:00:02.0389325
Void ExecuteParallel(): 00:00:00.6754025
Void ExecuteSerial(): 00:00:02.0653801
Void ExecuteParallel(): 00:00:00.7136330
Void ExecuteSerial(): 00:00:02.0477593
Void ExecuteParallel(): 00:00:00.6742260
Void ExecuteSerial(): 00:00:02.0476146

它的并行速度不如四核 i7 所期望的快,但我怀疑这是由于 Servy 提到的上下文切换等 - 也可能是 DLR 中的执行缓存争用。不过,它比串联运行要快。

自己尝试代码,看看你在你的盒子上得到了什么——但不是在调试器下。

代码:

using System;
using System.Diagnostics;
using System.Dynamic;
using System.Threading.Tasks;

class Test
{
    const int Iterations = 1000000;

    static void Main(string[] args)
    {
        for (int i = 0; i < 5; i++)
        {
            RunTest(ExecuteParallel);
            RunTest(ExecuteSerial);
        }

    }

    static void RunTest(Action action)
    {
        var sw = Stopwatch.StartNew();
        action();
        sw.Stop();
        Console.WriteLine("{0}: {1}", action.Method, sw.Elapsed);
    }

    static void ExecuteParallel()
    {
        var service = new Service();
        Parallel.For(0, Iterations, i => {
            dynamic user = new ExpandoObject();
            user.data = new ExpandoObject();
            user.data.id = i;
            user.data.name = "User Name";
            var parsed = service.Parse(user);
        });
    }

    static void ExecuteSerial()
    {
        var service = new Service();
        for (int i = 0; i < Iterations; i++)
        {
            dynamic user = new ExpandoObject();
            user.data = new ExpandoObject();
            user.data.id = i;
            user.data.name = "User Name";
            var parsed = service.Parse(user);
        }
    }
}

public class Service
{
    public User Parse(dynamic dynamicUser)
    {
        if (dynamicUser.data != null)
        {
            return new User
            {
                Id = dynamicUser.data.id,
                Name = dynamicUser.data.name
            };
        }
        return null;
    }
}

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
}
于 2012-10-19T20:23:09.607 回答
1

在这里,您正在执行的任务非常简单,花费的时间非常少,而且数量也足够少,以至于创建线程、分解任务、调度它们、处理上下文切换、内存屏障等等的开销其中,与您正在做的生产性工作量相比,这一点非常重要。如果你做的工作需要更长的时间,那么并行化的开销相比之下就会少得多。

于 2012-10-19T20:19:51.000 回答