【问题标题】:Different results when measuring .AsParallel()测量 .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 秒)
  • 我建议阅读 stackoverflow.com/questions/19388372/… 。 @Oliver 的建议将有助于减少“第一次调用总是较慢”效果的影响,从而进行真正的性能比较。

标签: c# multithreading performance linq plinq


【解决方案1】:

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-04-20
    • 1970-01-01
    • 1970-01-01
    • 2019-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-13
    相关资源
    最近更新 更多