动态对象并行性的表现

时间:2012-10-19 20:05:24

标签: c# task-parallel-library dynamic-language-runtime

以下代码大约在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的开销。

这是一个正确的假设,还是我离开基地?

2 个答案:

答案 0 :(得分: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; }
}

答案 1 :(得分:1)

这里你正在做的任务非常简单,花费的时间很少,而且只有很少的时间,创建线程,分解任务,安排任务,处理上下文切换,内存障碍的开销而且所有这一切与您正在进行的生产性工作量相比具有重要意义。如果你做的工作需要更长的时间,那么并行化的开销就会少得多。