首页 > 其他分享 >一个操作让数组处理速度快了5倍,到底是为什么

一个操作让数组处理速度快了5倍,到底是为什么

时间:2024-03-25 14:29:35浏览次数:23  
标签:处理速度 到底 int double elapsedTime 数组 stopwatch 排序 data

概述:通过对数组进行排序,代码更好地利用了缓存,从而提高了程序的性能。这种现象通常被称为"缓存友好"(cache-friendly)或"空间局部性"(spatial locality)

今天做一个数组数据计算时,发现一个效率问题,给大家分享一下 一个数组排序和不排序时同样的逻辑处理速度是不一样的。排序后速度快了近5倍,上图:

  1. 再来说明原因:

这段代码之所以在排序后运行更快,是因为它利用了现代计算机体系结构中的一个优化:CPU缓存。

在主循环中,对data数组的访问是顺序的,即按照数组元素的顺序依次访问。在没有排序的情况下,由于数组的内存布局是随机的,这可能导致对内存的随机访问,而这种随机访问可能导致较多的缓存缺失(cache misses)。

而在经过排序之后,数组的元素被重新排列,使得相邻元素的值更加接近。这就意味着在主循环中,对数组的访问会更加连续,这有助于提高缓存的命中率(cache hit rate)。高缓存命中率意味着CPU可以更快地获取数据,而不必等待缓慢的主内存。这对于循环中的迭代非常重要,因为它会不断地访问数组的不同部分。

通过对数组进行排序,代码更好地利用了缓存,从而提高了程序的性能。这种现象通常被称为"缓存友好"(cache-friendly)或"空间局部性"(spatial locality)。

  1. 然后来看看实际测试代码,不排序测试:
        static void Main()
        {
            double elapsedTime = Test1();
            double elapsedTime2 = Test2();

            Console.WriteLine($"排序前后:Test1/Test2={(double)(elapsedTime / elapsedTime2)}");
            Console.ReadKey();
        }

        /// <summary>
        /// 不排序测试
        /// </summary>
        static double Test1()
        {
            // 生成数据
            const int arraySize = 32768;
            int[] data = new int[arraySize];
            Random rand = new Random();

            for (int c = 0; c < arraySize; ++c)
                data[c] = rand.Next(256);  // 生成0-255的随机数

            // 测试
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            long sum = 0;
            for (int i = 0; i < 100000; ++i)
            {
                for (int c = 0; c < arraySize; ++c)
                {   // 主循环
                    if (data[c] >= 128)
                        sum += data[c];  // 如果数据大于等于128,则加到总和中
                }
            }

            stopwatch.Stop();
            double elapsedTime = stopwatch.ElapsedMilliseconds;  // 计算所花费的时间

            Console.WriteLine($"不排序效果:用时{elapsedTime}毫秒");  // 输出所花费的时间
            Console.WriteLine("sum = " + sum);  // 输出总和
            Console.WriteLine();
            return elapsedTime;
        }
  1. 排序后的测试代码:
        /// <summary>
        /// 排序测试
        /// </summary>
        /// <returns></returns>
        static double Test2()
        {
            // 生成数据
            const int arraySize = 32768;
            int[] data = new int[arraySize];
            Random rand = new Random();

            for (int c = 0; c < arraySize; ++c)
                data[c] = rand.Next(256);  // 生成0-255的随机数


            double elapsedTime = 0;
            // 测试
            Stopwatch stopwatch = new Stopwatch();
            stopwatch.Start();

            // 对数据进行排序,这样下一个循环会运行得更快
            Array.Sort(data);
            stopwatch.Stop();
            elapsedTime = stopwatch.ElapsedMilliseconds;  // 计算所花费的时间
            stopwatch.Restart();

            long sum = 0;
            for (int i = 0; i < 100000; ++i)
            {
                for (int c = 0; c < arraySize; ++c)
                {   // 主循环
                    if (data[c] >= 128)
                        sum += data[c];  // 如果数据大于等于128,则加到总和中
                }
            }

            stopwatch.Stop();
            double elapsedTime2 = stopwatch.ElapsedMilliseconds;  // 计算所花费的时间

            double elapsedTime3 = (elapsedTime + elapsedTime2);

            Console.WriteLine($"排序后效果:排序用时{elapsedTime}毫秒,计算用时:{elapsedTime2}毫秒,合计用时:{(elapsedTime3)}毫秒");  // 输出所花费的时间
            Console.WriteLine("sum = " + sum);  // 输出总和
            Console.WriteLine();

            return elapsedTime3;
        }

大家在Java、C++、Python是不是也遇到过类似的问题。

源代码获取:https://pan.baidu.com/s/1vm6faDdFFGFEmvpLMPATcQ?pwd=6666 

标签:处理速度,到底,int,double,elapsedTime,数组,stopwatch,排序,data
From: https://blog.csdn.net/lijingguang/article/details/137005990

相关文章

  • await 到底在等什么?
    核心其实await本质上等的是:后面的thenable对象的then方法调用resolve或者reject。这里面其实包含三个细节:thenable对象其实就是包含then方法的普通对象。如果await后面的对象不是一个thenable对象,那么系统会将它包装成thenable对象。Promise对象具有the......
  • 为什么使用类型化数组来进行字节操作而不是普通的 javascript 数字数组
    1.javascript中的数字数据类型默认为64位(8字节),无论任何数字。这意味着可以在不损失精度的情况下表示-2⁵³+1到2⁵³–1范围内的数字。这意味着即使我们想存储10个,也会消耗8个字节的内存,而这是根本不需要的。当内存效率是一个问题时,特别是在处理大型整数数组或二进制数......
  • c语言 实现切片数组
    c语言集合类第一章切片(本章)第二章栈文章目录c语言集合类前言一、接口定义1、创建切片2、销毁切片3、添加元素4、切片长度5、切片容量二、完整代码三、使用示例1、一般使用流程2、直接append3、自定义类型总结前言由于c语言没有集合类的标准库,需要用时只能自......
  • 04. Java 数组
    数组在计算机语言中是非常重要的集合类型,大部分计算机语言中数组具有如下三个基本特性:一致性:数组只能保存相同数据类型元素,元素的数据类型可以是任何相同的数据类型。有序性:数组中的元素是有序的,通过下标访问。不可变性:数组一旦初始化,则长度(数组中预分配的元素个数)不可变。......
  • 代码随想录算法训练营Day52 ||leetCode 300.最长递增子序列 || 674. 最长连续递增序列
    300.最长递增子序列 classSolution{public:intlengthOfLIS(vector<int>&nums){if(nums.size()<=1)returnnums.size();vector<int>dp(nums.size(),1);intresult=0;for(inti=1;i<nums.size......
  • 数组的度与子数组长度
    publicstaticintdegreeOfArray(List<int>arr){intcount=arr.Count;int[]freQue=newint[count];//Dictionary<int,int>freQue=newDictionary<int,int>();for(inti=0;i<count;i++)......
  • 动态数组类及其模板
    先定义point类,再定义由point类的动态数组#include<iostream>#include<cassert>usingnamespacestd;classPoint{private:intx,y;public:Point():x(0),y(0){cout<<"PointDefaultConstructorcalled."<<endl;}Poin......
  • arguments 这种类数组如何遍历
    类数组对象所谓的类数组对象:拥有一个length属性和若干索引属性的对象举个例子:vararray=['name','age','sex'];vararrayLike={0:'name',1:'age',2:'sex',length:3}即便如此,为什么叫做类数组对象呢?那让我们从读写、获取长度、遍......
  • Go数组的扩容规则
    Go数组的扩容规则Go数组的扩容规则技术要点先是双倍扩容,然后是一定的比例扩容,逐渐向1.25进行靠近在目前的实现里面,在小于256的时候会进行double,在大于256的时候,会根据一定的生长因子进行扩容,但是总体来说还是会逐渐的靠近到1.25funcgrowslice(et*_type,olds......
  • 一个操作让数组处理速度快了5倍,到底是为什么
     概述:通过对数组进行排序,代码更好地利用了缓存,从而提高了程序的性能。这种现象通常被称为"缓存友好"(cache-friendly)或"空间局部性"(spatiallocality)今天做一个数组数据计算时,发现一个效率问题,给大家分享一下一个数组排序和不排序时同样的逻辑处理速度是不一样的。排序后速度......