使用記憶體映射文件MMF實現大數據量導出時的記憶體優化(Linux篇)

来源:https://www.cnblogs.com/y-yp/archive/2020/02/28/12372963.html
-Advertisement-
Play Games

前言 今天這篇博客是接我的上一篇博客 https://www.cnblogs.com/y-yp/p/12191258.html,繼續介紹一下MMF在Linux上的用法 ps:本來本地調試完case,想放到伺服器上跑跑看,結果竟然報"PlatformNotSupportedException",然後仔 ...


前言

    今天這篇博客是接我的上一篇博客 https://www.cnblogs.com/y-yp/p/12191258.html,繼續介紹一下MMF在Linux上的用法

    ps:本來本地調試完case,想放到伺服器上跑跑看,結果竟然報"PlatformNotSupportedException",然後仔細一查,竟然發現MMF在Windows和Linux上的用法不一樣。。。"mapName"參數僅作為Window平臺的一個特性,在Linux平臺上只能傳"null",於是就有了今天這篇博客

實現

     既然“mapName”不能使用,經過測試選定使用了FileStream的這個重載

 

     具體細節就不在介紹了,有疑問的話可以參考我的上一篇博客 https://www.cnblogs.com/y-yp/p/12191258.html,這裡直接給實現

     先定義“行數據信息記錄“,這個用來讀取數據的時候用,一行數據只生成一條記錄,所以在大數據量的情況下也不會占用很多記憶體

        public class RowInfo
        {
            /// <summary>
            /// 行數據體積(單位位元組)
            /// </summary>
            public long Capacity { get; set; }

            /// <summary>
            /// 行單元格個數
            /// </summary>
            public int CellQuantity { get; set; }
        }

  然後開始將數據寫入MMF文件,並獲取到”行數據信息記錄“

            //準備數據
            var data = new List<string[]>();
            for (var i = 0; i < 100; i++)
            {
                var rowData = new string[100];
                for (var j = 0; j < 100; j++)
                {
                    rowData[j] = $"{i}-{j}";
                }
                data.Add(rowData);
            }

            //統計mmf文件體積,包含單元格數據的體積Encoding.UTF8.GetBytes(x).Length和預設單元格數據長度int類型占4位元組
            var mmfCapacity = data.Sum(x => x.Sum(x => Encoding.UTF8.GetBytes(x).Length + 4));
            var path = Environment.CurrentDirectory + "\\" + "test.txt";
            using var writerFs = new FileStream(path, FileMode.Create, FileAccess.ReadWrite);
            using var writerMMF = MemoryMappedFile.CreateFromFile(writerFs, null, mmfCapacity, MemoryMappedFileAccess.ReadWrite, HandleInheritability.Inheritable, true);

            //記錄行數據信息
            var rowInfos = new List<RowInfo>();
            var totalWriterOffset = 0;
            foreach (var rowData in data)
            {
                var rowBuffers = rowData.Select(x => Encoding.UTF8.GetBytes(x)).ToList();
                //計算行數據總體積
                var capacity = rowBuffers.Sum(x => x.Length + 4);
                //根據當前偏移和需要讀取的長度創建accessor
                using var accessor = writerMMF.CreateViewAccessor(totalWriterOffset, capacity);
                //統計同行內單元格偏移
                var offset = 0L;
                foreach (var cellBuffer in rowBuffers)
                {
                    if (cellBuffer.Length > 0)
                    {
                        var dataSize = cellBuffer.Length;
                        accessor.Write(offset, dataSize);
                        accessor.WriteArray(offset + 4, cellBuffer, 0, dataSize);
                        offset += 4 + dataSize;
                    }
                    else
                    {
                        accessor.Write(offset, 0);
                        offset += 4;
                    }
                }

                //記錄行數據信息
                var rowInfo = new RowInfo()
                {
                    Capacity = capacity,
                    CellQuantity = rowBuffers.Count()
                };
                rowInfos.Add(rowInfo);
                //總位移向前走一行數據的長度
                totalWriterOffset += capacity;
            }

            return rowInfos;

  通過”行數據信息記錄“還原數據,這裡可以將讀取出來的數據寫入自己的excel或者是csv文件,不再贅述

            var result = new List<string[]>();

            var path = Environment.CurrentDirectory + "\\" + "test.txt";
            //從行數據信息記錄統計mmf文件總體積
            var mmfCapacity = rowInfos.Sum(x => x.Capacity);
            var totalReaderOffset = 0;
            using var readerFs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite);
            using var readerMMF = MemoryMappedFile.CreateFromFile(readerFs, null, mmfCapacity, MemoryMappedFileAccess.ReadWrite, HandleInheritability.Inheritable, true);
            foreach(var rowInfo in rowInfos)
            {
                var rowData = new string[rowInfo.CellQuantity];
                using var accessor = readerMMF.CreateViewAccessor(totalReaderOffset, rowInfo.Capacity);
                var position = 0;
                for (int cellIndex = 0; cellIndex < rowInfo.CellQuantity; cellIndex++)
                {

                    var cellSize = accessor.ReadInt32(position);
                    var buffer = new byte[cellSize];
                    accessor.ReadArray(position + 4, buffer, 0, cellSize);
                    rowData[cellIndex] = Encoding.UTF8.GetString(buffer);
                    position += 4 + cellSize;
                }
                result.Add(rowData);
            }

            if (File.Exists(path))
            {
                File.Delete(path);
            }

  考慮使用記憶體映射文件的話,可以先本地測試一下性能,如果是SSD的話性能還是很不錯的,綜合跑下來跟直接寫入記憶體速度相差不會超過一兩倍(記憶體使用率較高的話會嚴重降低性能,甚至會OOM),而且這其中還有很多的優化空間

       今天的文章只是提了個思路,細節還有很多要考慮,有疑問的話歡迎提問交流~~

 


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

-Advertisement-
Play Games
更多相關文章
  • static void CheckedUnCheckedDemo() { int i = int.MaxValue; try { //checked //{ // Console.WriteLine(i + 1); //} unchecked { Console.WriteLine(i + 1); ...
  • A finally block does not always xecute. The code in the try block could go into an infinite loop, the exception could rigger a “fail fast” (which take ...
  • 項目需要(或者前後端分離的需要),前端我使用了用戶控制項庫,由後端用代碼載入和控制。 然而用戶控制項庫沒法指定資源字典,於是在用戶控制項的xaml文件裡面手工添加了資源字典 設計階段方便了,生成dll,被主程式調用的時候,就報錯了,說沒有該資源文件(d1.xaml),研究Pack Url後明白,可以有兩種 ...
  • C#中實現文件拖放打開的方法 設置Form屬性 AllowDrop = True; 在Form事件中 private void Form1_DragDrop(object sender, DragEventArgs e) { string localFilePath = ((System.Array ...
  • 修改註冊表,雙擊文件直接打開 string strProject = "Exec"; string p_FileTypeName =".cdb";//文件尾碼 string fileName = System.Windows.Forms.Application.ExecutablePath;// 獲 ...
  • 作者: 魔法軟糖 日期: 2020-02-27 引言 ************************************* .ini 文件是Initialization File的縮寫,即配置文件 。是windows的系統配置文件所採用的存儲格式。 它具有方便易用的特點,和註冊表的鍵值有著類似 ...
  • 最近開始試著玩Unity3D,要為場景中的物體編輯腳本。Unity3D推薦的腳本語言是C#,在Unity打開C#就會使用Visual Studio來進行編輯。 啟動Visual Studio之後註意到,Unity類和方法名都沒有代碼補全,而且Unity似乎也編譯不過: 在網上找了一圈,發現並沒有人遇 ...
  • 在 C 中 Object 是所有類的基類,所有的結構和類都直接或間接的派生自它。前面這段話可以說所有的 C 開發人員都知道,但是我相信其中有一部分程式員並不清楚甚至不知道我們常用的 ToString 、 Equals 和 GetHashCode 虛方法都來自於 Object 類,並且我們可以對它們進 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...