一個操作讓數組處理速度快了5倍,到底是為什麼

来源:https://www.cnblogs.com/hanbing81868164/p/18092404
-Advertisement-
Play Games

概述:通過對數組進行排序,代碼更好地利用了緩存,從而提高了程式的性能。這種現象通常被稱為"緩存友好"(cache-friendly)或"空間局部性"(spatial locality) 今天做一個數組數據計算時,發現一個效率問題,給大家分享一下 一個數組排序和不排序時同樣的邏輯處理速度是不一樣的。排 ...


 

概述:通過對數組進行排序,代碼更好地利用了緩存,從而提高了程式的性能。這種現象通常被稱為"緩存友好"(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 

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕等都可能不太一樣。 2、表單的靈活設計及呈現。 3、流程的靈活設計及呈現。 4、介面的調用信息者及性能 ...
  • 概述:WPF支持綁定到對象的屬性而不是欄位,主要因為屬性提供了更多控制和擴展性。屬性包含get和set方法,支持數據驗證和通知屬性更改,而欄位通常被認為是內部實現。使用屬性使WPF能夠更靈活、可控地與數據交互,提高代碼的可讀性和可維護性。 WPF(Windows Presentation Found ...
  • 概述:上述C#示例演示瞭如何在同步方法中調用非同步方法。通過使用`async`和`await`關鍵字,實現了同步方法對非同步方法的調用。建議使用`await`而不是`Result`來避免潛在的死鎖問題。這種模式在處理非同步任務時能夠提高代碼的可讀性和性能。 在C#中,從同步方法調用非同步方法的過程涉及到使用 ...
  • 一:背景 1. 講故事 前些天有一位朋友在公眾號上找到我,說他們的WinForm程式部署在20多台機器上,只有兩台機器上的程式會出現崩潰的情況,自己找了好久也沒分析出來,讓我幫忙看下怎麼回事,就喜歡這些有點調試基礎的,dump也不需要我指導怎麼去抓,接下來我們就上windbg開始分析吧。 二:Win ...
  • 構建類型是動態代碼生成過程中的關鍵一環,通過靈活運用 TypeBuilder 和相關工具,我們可以實現各種複雜類型的動態生成,為程式的靈活性和可擴展性提供有力支持。總的來說,本章節通過演示如何使用 Emit 來動態創建類型,包括定義欄位、方法、屬性和事件等, 幫助讀者理解如何在運行時生成和操作類型信... ...
  • 前言 本文的主題是 Visual Studio 調試 NuGet 包,以及符號包的概念,如何去發佈一個 NuGet 包,讓我們的 NuGet 包支持 SouceLink,這些都是我們開發中比較容易忽視的內容,但是熟悉了以後可以讓我們在調試中得心應手,也是開源項目開發的基礎篇。 demo 源代碼在文末 ...
  • 在Orleans 7中,Grain放置是指確定將Grain對象放置在Orleans集群中的哪些物理節點上的過程。 Grain是Orleans中的基本單位,代表應用程式中的邏輯單元或實體。Grain放置策略是一種機制,用於根據不同的因素,將Grain對象放置在合適的節點上,以實現負載均衡、最小化網路延 ...
  • 在之前的WPF示例中,都會用到一個MVVM框,也是一個比較常的MVVM框架,就是MVVM工具包【CommunityToolkit.Mvvm】,今天專門以一個簡單的小例子,簡述一下MVVM工具包的常見用法,僅供學習分享使用,如有不足之處,還請指正。 ...
一周排行
    -Advertisement-
    Play Games
  • 1、預覽地址:http://139.155.137.144:9012 2、qq群:801913255 一、前言 隨著網路的發展,企業對於信息系統數據的保密工作愈發重視,不同身份、角色對於數據的訪問許可權都應該大相徑庭。 列如 1、不同登錄人員對一個數據列表的可見度是不一樣的,如數據列、數據行、數據按鈕 ...
  • 前言 上一篇文章寫瞭如何使用RabbitMQ做個簡單的發送郵件項目,然後評論也是比較多,也是準備去學習一下如何確保RabbitMQ的消息可靠性,但是由於時間原因,先來說說設計模式中的簡單工廠模式吧! 在瞭解簡單工廠模式之前,我們要知道C#是一款面向對象的高級程式語言。它有3大特性,封裝、繼承、多態。 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 介紹 Nodify是一個WPF基於節點的編輯器控制項,其中包含一系列節點、連接和連接器組件,旨在簡化構建基於節點的工具的過程 ...
  • 創建一個webapi項目做測試使用。 創建新控制器,搭建一個基礎框架,包括獲取當天日期、wiki的請求地址等 創建一個Http請求幫助類以及方法,用於獲取指定URL的信息 使用http請求訪問指定url,先運行一下,看看返回的內容。內容如圖右邊所示,實際上是一個Json數據。我們主要解析 大事記 部 ...
  • 最近在不少自媒體上看到有關.NET與C#的資訊與評價,感覺大家對.NET與C#還是不太瞭解,尤其是對2016年6月發佈的跨平臺.NET Core 1.0,更是知之甚少。在考慮一番之後,還是決定寫點東西總結一下,也回顧一下.NET的發展歷史。 首先,你沒看錯,.NET是跨平臺的,可以在Windows、 ...
  • Nodify學習 一:介紹與使用 - 可樂_加冰 - 博客園 (cnblogs.com) Nodify學習 二:添加節點 - 可樂_加冰 - 博客園 (cnblogs.com) 添加節點(nodes) 通過上一篇我們已經創建好了編輯器實例現在我們為編輯器添加一個節點 添加model和viewmode ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...
  • 類型檢查和轉換:當你需要檢查對象是否為特定類型,並且希望在同一時間內將其轉換為那個類型時,模式匹配提供了一種更簡潔的方式來完成這一任務,避免了使用傳統的as和is操作符後還需要進行額外的null檢查。 複雜條件邏輯:在處理複雜的條件邏輯時,特別是涉及到多個條件和類型的情況下,使用模式匹配可以使代碼更 ...
  • 在日常開發中,我們經常需要和文件打交道,特別是桌面開發,有時候就會需要載入大批量的文件,而且可能還會存在部分文件缺失的情況,那麼如何才能快速的判斷文件是否存在呢?如果處理不當的,且文件數量比較多的時候,可能會造成卡頓等情況,進而影響程式的使用體驗。今天就以一個簡單的小例子,簡述兩種不同的判斷文件是否... ...
  • 前言 資料庫併發,數據審計和軟刪除一直是數據持久化方面的經典問題。早些時候,這些工作需要手寫複雜的SQL或者通過存儲過程和觸發器實現。手寫複雜SQL對軟體可維護性構成了相當大的挑戰,隨著SQL字數的變多,用到的嵌套和複雜語法增加,可讀性和可維護性的難度是幾何級暴漲。因此如何在實現功能的同時控制這些S ...