测量 .AsParallel() 时的不同结果

Posted

技术标签:

【中文标题】测量 .AsParallel() 时的不同结果【英文标题】:Different results when measuring .AsParallel() 【发布时间】:2019-02-08 03:23:37 【问题描述】:

我似乎找不到答案的问题是:为什么如果我更改调用包含.AsParallel() 的方法和不包含或具有不同选项的方法的顺序,结果会不同.

我已将源代码放在 GitHub 上,所以我不会填满整个屏幕 -> https://github.com/moisoiu/asParallel

还有一些关于我到目前为止如何测试它的说明。

我正在使用其他类来生成一些随机数据,从那时起,我将生成的随机数据传递给 ParallelThreading 方法,并通过 StopWatch 类测量时间并将结果存储在字典中在结束时显示响应时间。

起初我假设性能时间的问题,可能是因为我在同一个引用上工作,所以我创建了 4 个新对象 something 这是一个数组,所以它会有新的参考,但结果我们是一样的。

我什至假设也许(只是也许)来自 watch 的变量可能仍会存储前一个变量的值,所以我为每个变量创建了一个新变量

所以有什么想法吗?

只需从 Program.cs 中选择一个方法,然后将其与其他方法交换,就会有几乎相同的响应时间,但持续时间最长的方法将是 TOP 的方法,而较少的方法将在底部.

编辑: 尊重以下 cmets 的建议。我附上了部分源代码,如果需要更多,我会更新并从上面的Git中删除链接。

程序.cs

 class Program

    static void Main(string[] args)
    
        var parallelThreading = new ParallelThreading();
        long elapsedMs = 0;
        Dictionary<string, long> results = new Dictionary<string, long>();            

        var dataResults = parallelThreading.InitializeDataForParallelData(6500);
        var something = new PersonModel[6500];
        var something1 = new PersonModel[6500];
        var something2 = new PersonModel[6500];
        var something3 = new PersonModel[6500];
        dataResults.CopyTo(something);
        dataResults.CopyTo(something1);
        dataResults.CopyTo(something2);
        dataResults.CopyTo(something3);

        var watch2 = System.Diagnostics.Stopwatch.StartNew();
        parallelThreading.AsParallelAddingParallelization(something1.ToList());
        watch2.Stop();
        elapsedMs = watch2.ElapsedMilliseconds;
        results.Add(nameof(parallelThreading.AsParallelAddingParallelization), elapsedMs);

        var watch = System.Diagnostics.Stopwatch.StartNew();
        parallelThreading.AsParallel(something2.ToList());
        watch.Stop();
        elapsedMs = watch.ElapsedMilliseconds;
        results.Add(nameof(parallelThreading.AsParallel), elapsedMs);

        var watch1 = System.Diagnostics.Stopwatch.StartNew();
        parallelThreading.WithoutAsParallel(something3.ToList());
        watch1.Stop();
        elapsedMs = watch1.ElapsedMilliseconds;
        results.Add(nameof(parallelThreading.WithoutAsParallel), elapsedMs);

        var watch3 = System.Diagnostics.Stopwatch.StartNew();
        parallelThreading.AsParallelAsOrdered(something.ToList());
        watch3.Stop();
        elapsedMs = watch3.ElapsedMilliseconds;
        results.Add(nameof(parallelThreading.AsParallelAsOrdered), elapsedMs);


        foreach (var result in results)
        
            WriteTime(result.Key, result.Value);
        

        Console.ReadLine();
    

    private static void WriteTime(string methodName, long elapsedMiliseconds)
    
        Console.WriteLine($" methodName: Milisecond passed:  elapsedMiliseconds");
    

ParallelThreading.cs

 public class ParallelThreading


    private List<PersonModel> RandomData(int range)
    
        List<PersonModel> personModels = new List<PersonModel>();
        for (int i = 0; i < range; i++)
        
            var person = new PersonModel()
            
                NumberIdentification = i,
                Age = Convert.ToInt32(GenerateString.RandomNumbers(2)),
                DateOfBirth = GenerateString.GenerateDateTime(),
                LastName = GenerateString.RandomString(10),
                Name = GenerateString.RandomString(10),
                City = GenerateString.RandomString(10)
            ;
            personModels.Add(person);
        
        return personModels;
    

    private List<PersonModel> RandomDataWithSpecificSameData(int range, string city)
    

        List<PersonModel> personModels = new List<PersonModel>();
        for (int i = 0; i < range; i++)
        
            var person = new PersonModel();
            if (GenerateString.random.Next(range - 1) % 2 == 0)
            
                person = new PersonModel()
                
                    NumberIdentification = i,
                    Age = Convert.ToInt32(GenerateString.RandomNumbers(2)),
                    DateOfBirth = GenerateString.GenerateDateTime(),
                    LastName = GenerateString.RandomString(10),
                    Name = GenerateString.RandomString(10),
                    City = city
                ;
            
            else
            
                person = new PersonModel()
                
                    NumberIdentification = i,
                    Age = Convert.ToInt32(GenerateString.RandomNumbers(2)),
                    DateOfBirth = GenerateString.GenerateDateTime(),
                    LastName = GenerateString.RandomString(10),
                    Name = GenerateString.RandomString(10),
                    City = GenerateString.RandomString(10),                        
                ;
            
            personModels.Add(person);
        
        return personModels;
    


    #region AsParallelLINQ

    public List<PersonModel> InitializeDataForParallelData(int range)
    
        return RandomDataWithSpecificSameData(range, "Oradea");
    

    public void AsParallel(List<PersonModel> data)
    
        var result = data
            .AsParallel()
            .Where(c => c.City == "Oradea")
            .Select(c => c);
        foreach (var person in result)
        
            Console.WriteLine($"person.NumberIdentification + person.Name + person.City");
        
    

    public void AsParallelAddingParallelization(List<PersonModel> data)
    
        var result = data
            .AsParallel()
            .WithDegreeOfParallelism(8)
            .WithExecutionMode(ParallelExecutionMode.ForceParallelism)
            .Where(c => c.City == "Oradea")
            .Select(c => c);
        foreach (var person in result)
        
            Console.WriteLine($"person.NumberIdentification + person.Name + person.City");
        
    

    public void AsParallelAsOrdered(List<PersonModel> data)
    
        var result = data
            .AsParallel()
            .AsOrdered()
            .Where(c => c.City == "Oradea")
            .Select(c => c);
        foreach (var person in result)
        
            Console.WriteLine($"person.NumberIdentification + person.Name + person.City");
        
    

    public void WithoutAsParallel(List<PersonModel> data)
    
        var result = data.Where(c => c.City == "Oradea").Select(c => c);
        foreach (var person in result)
        
            Console.WriteLine($"person.NumberIdentification + person.Name + person.City");
        
    

    #endregion


【问题讨论】:

原因是我不想用源代码填充屏幕,因为它不会帮助任何人,将代码屏幕或其他屏幕之间的点连接起来。正如我在上面的帖子中提到的预期结果是,为什么如果我交换方法我仍然得到几乎相同的响应时间?我在 Debug 中进行的测试,它的版本是 .Net Core 版本 2.1.402 如果您需要对代码进行基准测试,您可能应该使用github.com/dotnet/BenchmarkDotNet 以避免在尝试测量代码性能时常见的陷阱。 mjwills 我同意,但 Debug 或 Release 之间没有太大区别。 @Magnus 我所期望的是,每当我运行一个方法(例如 AsParallelAddingParallelization() )以获得相同的经过毫秒数时。如果我交换方法(例如,最后一个与第一个),也不会有不同的时间 Oliver 感谢您的建议,我会试一试。 抱歉之前没有看到链接。我将首先回答您在评论中提出的问题-> 为什么后面的方法调用更快“?正确吗?如果您将 docs.microsoft.com/en-us/dotnet/api/... 设置为50?不完全是,我试图解释为什么如果我调用顺序方法(例如:a,b,c,d)它需要从最多到最少的过程。(例如:a = 3 秒,b = 2 秒,c = 1 秒和 d = 0.5 秒),如果我交换方法,时间保持几乎相同(例如:d = 3 秒,b = 2 秒,c = 1 秒a = 0.5 秒) 我建议阅读 ***.com/questions/19388372/… 。 @Oliver 的建议将有助于减少“第一次调用总是较慢”效果的影响,从而进行真正的性能比较。 【参考方案1】:

非常感谢 @mjwills 和 @Oliver 提供的帮助和链接。

在略过BenchmarkDotNet 的表面(有很多选项)并对解决方案进行一些调整之后,似乎(至少)就我而言,这些方法之间没有区别(但那是一个不同的问题,关于 AsParallel 应该如何、何时以及使用什么类型的数据来获得性能)。

第一篇文章的评论总结: 我担心的是为什么方法从上到下调用​​的时间间隔从最长到最短,即使我交换方法(例如,底部的最后一个与顶部的第一个)

所以,幕后是因为第一次有一个“预热”过程,其中涉及不同操作的缓存机制*** link to details

所以作为解决问题的方法:使用 BenchmarkDotNet,不要忘记缓存操作(上面提到的那个)

【讨论】:

以上是关于测量 .AsParallel() 时的不同结果的主要内容,如果未能解决你的问题,请参考以下文章

交流电压表

在实体框架中将行转换为列

AsParallel() 的最大并行度

在 IEnumerable 上使用 AsParallel - 它是不是有任何并行性的好处?

Parallel.ForEach() 与 foreach(IEnumerable<T>.AsParallel())

在 C# 中列出文件夹内的重复文件:利用 LINQ.AsParallel