3

我写了一个Stopwatch用于分析方法和for/foreach循环的类。使用forandforeach循环它会针对 a Parallel.FororParallel.ForEach实现测试标准循环。

你会像这样编写性能测试:

方法:

PerformanceResult result = Profiler.Execute(() => { FooBar(); });

对于循环:

SerialParallelPerformanceResult result = Profiler.For(0, 100, x => { FooBar(x); });

ForEach 循环:

SerialParallelPerformanceResult result = Profiler.ForEach(list, item => { FooBar(item); });

每当我运行测试(其中一个.Execute.For.ForEach)时,我都会将它们放在一个循环中,这样我就可以看到性能如何随时间变化。

性能示例可能是:

方法执行 1 = 200ms
方法执行 2 = 12ms
方法执行 3 = 0ms

执行时 1 = 300ms(串行)、100ms(并行)
执行时 2 = 20ms(串行)、75ms(并行)
执行时 3 = 2ms(串行)、50ms(并行)

ForEach 执行 1 = 350ms(串行),300ms(并行)
ForEach 执行 2 = 24ms(串行),89ms(并行)
ForEach 执行 3 = 1ms(串行),21ms(并行)

我的问题是:

  1. 为什么性能会随着时间而变化,.NET 在后台做了什么来促进这一点?

  2. 串行操作如何/为什么比并行操作更快?我已确保我使操作复杂化以正确查看差异......在大多数情况下,串行操作似乎更快!?

注意:对于并行处理,我正在 8 核机器上进行测试。

4

2 回答 2

4

在对性能分析进行了更多探索之后,我发现使用秒表并不是衡量特定任务性能的准确方法

(感谢斧头和 Loren 对此的评论!)

秒表不准确的原因:

  1. 测量以经过的时间(以毫秒为单位)计算,而不是 CPU 时间。
  2. 测量可能会受到背景“噪音”和线程密集型进程的影响。
  3. 测量不考虑 JIT 编译和开销。

话虽如此,使用秒表可以随意探索性能。考虑到这一点,我在一定程度上改进了我的分析算法。

在它只是简单地执行传递给它的表达式之前,它现在可以对表达式进行多次迭代,从而建立平均执行时间。第一次运行可以省略,因为这是 JIT 发挥作用的地方,并且可能会出现一些主要开销。可以理解,这永远不会像使用像Redgate 的 ANTS profiler这样的专业分析工具那样复杂,但对于更简单的任务来说没问题!

于 2012-09-28T09:36:59.757 回答
2

根据我上面的评论:我自己做了一些简单的测试,随着时间的推移没有发现任何差异。你能分享你的代码吗?我会把我的答案放在一个答案中,因为它不适合这里。

这是我的示例代码。(我也尝试了静态和实例方法,没有区别)

class Program
{
    static void Main(string[] args)
    {
        int to = 50000000;
        OtherStuff os = new OtherStuff();

        Console.WriteLine(Profile(() => os.CountTo(to)));
        Console.WriteLine(Profile(() => os.CountTo(to)));
        Console.WriteLine(Profile(() => os.CountTo(to)));
    }

    static long Profile(Action method)
    {
        Stopwatch st = Stopwatch.StartNew();
        method();
        st.Stop();
        return st.ElapsedMilliseconds;
    }
}

class OtherStuff
{
    public void CountTo(int to)
    {
        for (int i = 0; i < to; i++)
        {
            // some work...
            i++;
            i--;
        }
    }
}

示例输出为:

331
331
334

考虑改为执行此方法:

class OtherStuff
    {
        public string CountTo(Guid id)
        {
            using(SHA256 sha = SHA256.Create())
            {
                int x = default(int);
                for (int index = 0; index < 16; index++)
                {
                    x = id.ToByteArray()[index] >> 32 << 16;
                }
                RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
                byte[] y = new byte[1024];
                rng.GetBytes(y);
                y = y.Concat(BitConverter.GetBytes(x)).ToArray();
                return BitConverter.ToString(sha.ComputeHash(BitConverter.GetBytes(x).Where(o => o >> 2 < 0).ToArray()));
            }
        }
    }

样本输出:

11 
0 
0
于 2015-01-27T19:10:11.737 回答