生成CPU缓存失效时的性能表现

14

我正试图学习在.NET世界中CPU缓存性能。具体来说,我正在阅读Igor Ostovsky的有关处理器缓存效应的文章

我已经完成了他文章中的前三个示例,并记录了结果,这些结果与他的结果大相径庭。我认为我一定做错了什么,因为我的机器性能显示出与他在文章中展示的几乎完全相反的结果。我没有看到我所期望的由于缓存未命中而产生的巨大影响。

我做错了什么?(糟糕的代码、编译器设置等)

以下是我机器上的性能结果:

enter image description here

enter image description here

enter image description here

如果有帮助的话,我的处理器是Intel Core i7-2630QM。以下是有关我的处理器缓存的信息:

enter image description here

我以x64 Release模式进行编译。

以下是我的源代码:

class Program
    {

        static Stopwatch watch = new Stopwatch();

        static int[] arr = new int[64 * 1024 * 1024];

        static void Main(string[] args)
        {
            Example1();
            Example2();
            Example3();


            Console.ReadLine();
        }

        static void Example1()
        {
            Console.WriteLine("Example 1:");

            // Loop 1
            watch.Restart();
            for (int i = 0; i < arr.Length; i++) arr[i] *= 3;
            watch.Stop();
            Console.WriteLine("     Loop 1: " + watch.ElapsedMilliseconds.ToString() + " ms");

            // Loop 2
            watch.Restart();
            for (int i = 0; i < arr.Length; i += 32) arr[i] *= 3;
            watch.Stop();
            Console.WriteLine("     Loop 2: " + watch.ElapsedMilliseconds.ToString() + " ms");

            Console.WriteLine();
        }

        static void Example2()
        {

            Console.WriteLine("Example 2:");

            for (int k = 1; k <= 1024; k *= 2)
            {

                watch.Restart();
                for (int i = 0; i < arr.Length; i += k) arr[i] *= 3;
                watch.Stop();
                Console.WriteLine("     K = "+ k + ": " + watch.ElapsedMilliseconds.ToString() + " ms");

            }
            Console.WriteLine();
        }

        static void Example3()
        {   

            Console.WriteLine("Example 3:");

            for (int k = 1; k <= 1024*1024; k *= 2)
            {

                //256* 4bytes per 32 bit int * k = k Kilobytes
                arr = new int[256*k];



                int steps = 64 * 1024 * 1024; // Arbitrary number of steps
                int lengthMod = arr.Length - 1;

                watch.Restart();
                for (int i = 0; i < steps; i++)
                {
                    arr[(i * 16) & lengthMod]++; // (x & lengthMod) is equal to (x % arr.Length)
                }

                watch.Stop();
                Console.WriteLine("     Array size = " + arr.Length * 4 + " bytes: " + (int)(watch.Elapsed.TotalMilliseconds * 1000000.0 / arr.Length) + " nanoseconds per element");

            }
            Console.WriteLine();
        }

    }

你使用的是什么CPU?它有多少缓存?一级和二级缓存分别是多少? - Oded
这是一款Intel Core i7-2630QM处理器。缓存统计数据在上面的命令行图像中。 - Jason Moore
另外,您的系统中有足够的RAM吗?测试期间没有在页面文件上进行过度交换吧? - Chris O
我的机器有8GB的RAM。运行这个控制台应用程序时,RAM使用量从未超过4GB。 - Jason Moore
2
你可以安全地假设作者的核心不如你的好。测试循环固有的1/K性能强调了他的结果,但混淆了你的结果。 - Hans Passant
1个回答

3
在第二个循环中,为什么要使用i += 32?这样会跨越缓存行。32*4=128字节,比需要的64字节大得多。

2
我不理解这个答案。为什么这能解释数量级的差异,这与第二或第三个测试有什么关系? - BlueRaja - Danny Pflughoeft
1
即使知道这已经很旧了,只是为了其他人的参考,缓存行通常以64字节的块获取,所以DiVan所展示的是,在一个int(4字节)数组中,无论您以32步长遍历它,您最终都会跳过多个缓存行,这当然会使循环2更快,如果您使用16而不是32(16x4=64),那么您将不会跳过任何缓存行,循环1和2将具有类似的结果,即使循环2迭代次数比循环1少。 - DenninDalke

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接