【類拷貝文件的運用】

常用示例

當我們面臨將文本文件分成最大大小塊的時,我們可能會嘗試編寫如下代碼:

public class TestSplit {private static final long maxFileSizeBytes = 10 * 1024 * 1024; // 默認10MBpublic void split(Path inputFile, Path outputDir) throws IOException {if (!Files.exists(inputFile)) {throw new IOException("輸入文件不存在: " + inputFile);}if (Files.size(inputFile) == 0) {throw new IOException("輸入文件為空: " + inputFile);}Files.createDirectories(outputDir);try (BufferedReader reader = Files.newBufferedReader(inputFile)) {int fileIndex = 0;long currentSize = 0;BufferedWriter writer = null;try {writer = newWriter(outputDir, fileIndex++);String line;while ((line = reader.readLine()) != null) {byte[] lineBytes = (line + System.lineSeparator()).getBytes();if (currentSize + lineBytes.length > maxFileSizeBytes) {if (writer != null) {writer.close();}writer = newWriter(outputDir, fileIndex++);currentSize = 0;}writer.write(line);writer.newLine();currentSize += lineBytes.length;}} finally {if (writer != null) {writer.close();}}}}private BufferedWriter newWriter(Path dir, int index) throws IOException {Path filePath = dir.resolve("part_" + index + ".txt");return Files.newBufferedWriter(filePath);}public static void main(String[] args) {String inputFilePath = "C:\Users\fei\Desktop\testTwo.txt";String outputDirPath = "C:\Users\fei\Desktop\testTwo";TestSplit splitter = new TestSplit();try {long startTime = System.currentTimeMillis();splitter.split(Paths.get(inputFilePath), Paths.get(outputDirPath));long endTime = System.currentTimeMillis();long duration = endTime - startTime;System.out.println("文件拆分完成!");System.out.printf("總耗時:%d 毫秒%n", duration);} catch (IOException e) {System.out.println("文件拆分過程中發生錯誤:" + e.getMessage());}}
}

效率分析

此代碼在技術上是可以的,但是將大文件拆分為多個塊的效率非常低。具體如下

  1. 它執行許多堆分配 (行),導致創建和丟棄大量臨時對象 (字符串、字節數組) 。
  2. 還有一個不太明顯的問題,它將數據復制到多個緩沖區,并在用戶和內核模式之間執行上下文切換。

代碼詳細分析

BufferedReader: BufferedReader 的 BufferedReader 中:

  • 在底層 FileReaderInputStreamReader 上調用 read()
  • 數據從內核空間用戶空間緩沖區復制。
  • 然后解析為 Java 字符串(堆分配)。

getBytes() : getBytes()

  • String 轉換為新的 byte[] →更多的堆分配。

BufferedWriter: BufferedWriter 的 BufferedWriter 中:

  • 從用戶空間獲取 byte/char 數據。
  • 調用 write()這又涉及將用戶空間復制到內核空間→。
  • 最終刷新到磁盤。

因此,數據在內核和用戶空間之間來回移動多次,并產生額外的堆改動。除了垃圾收集壓力外,它還具有以下后果:

  • 內存帶寬浪費在緩沖區之間進行復制。
  • 磁盤到磁盤傳輸的 CPU 利用率較高。
  • 操作系統本可直接處理批量拷貝(通過DMA或優化I/O),但Java代碼通過引入用戶空間邏輯攔截了這種高效性。

方案

那么,我們如何避免上述問題呢?

答案是盡可能使用 zero copy,即盡可能避免離開 kernel 空間。這可以通過使用 FileChannel 方法 long transferTo(long position, long count, WritableByteChannel target) 在 java 中完成。它直接是磁盤到磁盤的傳輸,還會利用作系統的一些 IO 優化。

有問題就是所描述的方法對字節塊進行作,可能會破壞行的完整性。為了解決這個問題,我們需要一種策略來確保即使通過移動字節段處理文件時,行也保持完整

沒有上述的問題就很容易,只需為每個塊調用 transferTo,將position遞增為 position = position + maxFileSize,直到無法傳輸更多數據。

為了保持行的完整性,我們需要確定每個字節塊中最后一個完整行的結尾。為此,我們首先查找 chunk 的預期末尾,然后向后掃描以找到前面的換行符。這將為我們提供 chunk 的準確字節計數,確保包含最后的、不間斷的行。這將是執行緩沖區分配和復制的代碼的唯一部分,并且由于這些作應該最小,因此預計性能影響可以忽略不計。

private static final int LINE_ENDING_SEARCH_WINDOW = 8 * 1024;
?
private long maxSizePerFileInBytes;
private Path outputDirectory;
private Path tempDir;
?
private void split(Path fileToSplit) throws IOException {try (RandomAccessFile raf = new RandomAccessFile(fileToSplit.toFile(), "r");FileChannel inputChannel = raf.getChannel()) {
?long fileSize = raf.length();long position = 0;int fileCounter = 1;
?while (position < fileSize) {// Calculate end position (try to get close to max size)long targetEndPosition = Math.min(position + maxSizePerFileInBytes, fileSize);
?// If we're not at the end of the file, find the last line ending before max sizelong endPosition = targetEndPosition;if (endPosition < fileSize) {endPosition = findLastLineEndBeforePosition(raf, position, targetEndPosition);}
?long chunkSize = endPosition - position;var outputFilePath = tempDir.resolve("_part" + fileCounter);try (FileOutputStream fos = new FileOutputStream(outputFilePath.toFile());FileChannel outputChannel = fos.getChannel()) {inputChannel.transferTo(position, chunkSize, outputChannel);}
?position = endPosition;fileCounter++;}
?}
}
?
private long findLastLineEndBeforePosition(RandomAccessFile raf, long startPosition, long maxPosition)throws IOException {long originalPosition = raf.getFilePointer();
?try {int bufferSize = LINE_ENDING_SEARCH_WINDOW;long chunkSize = maxPosition - startPosition;
?if (chunkSize < bufferSize) {bufferSize = (int) chunkSize;}
?byte[] buffer = new byte[bufferSize];long searchPos = maxPosition;
?while (searchPos > startPosition) {long distanceToStart = searchPos - startPosition;int bytesToRead = (int) Math.min(bufferSize, distanceToStart);
?long readStartPos = searchPos - bytesToRead;raf.seek(readStartPos);
?int bytesRead = raf.read(buffer, 0, bytesToRead);if (bytesRead <= 0)break;
?// Search backwards through the buffer for newlinefor (int i = bytesRead - 1; i >= 0; i--) {if (buffer[i] == '\n') {return readStartPos + i + 1;}}
?searchPos -= bytesRead;}
?throw new IllegalArgumentException("File " + fileToSplit + " cannot be split. No newline found within the limits.");} finally {raf.seek(originalPosition);}
}

findLastLineEndBeforePosition 方法具有某些限制。具體來說,它僅適用于類 Unix 系統 (\n),非常長的行可能會導致大量向后讀取迭代,并且包含超過 maxSizePerFileInBytes 的行的文件無法拆分。但是,它非常適合拆分訪問日志文件等場景,這些場景通常具有短行和大量條目。

性能分析

理論上,我們zero copy拆分文件應該【常用方式】更快,現在是時候衡量它能有多快了。為此,我為這兩個實現運行了一些基準測試,這些是結果。

Benchmark ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?  Mode  Cnt ? ? ? ? ? Score ? ?  Error ? Units
FileSplitterBenchmark.splitFile ? ? ? ? ? ? ? ? ? ? ? ? ? ?  avgt ? 15 ? ? ?  1179.429 ± ? 54.271 ? ms/op
FileSplitterBenchmark.splitFile:·gc.alloc.rate ? ? ? ? ? ? ? avgt ? 15 ? ? ?  1349.613 ± ? 60.903  MB/sec
FileSplitterBenchmark.splitFile:·gc.alloc.rate.norm ? ? ? ?  avgt ? 15  1694927403.481 ± 6060.581 ?  B/op
FileSplitterBenchmark.splitFile:·gc.count ? ? ? ? ? ? ? ? ?  avgt ? 15 ? ? ? ? 718.000 ? ? ? ? ? ? counts
FileSplitterBenchmark.splitFile:·gc.time ? ? ? ? ? ? ? ? ? ? avgt ? 15 ? ? ? ? 317.000 ? ? ? ? ? ? ? ? ms
FileSplitterBenchmark.splitFileZeroCopy ? ? ? ? ? ? ? ? ? ?  avgt ? 15 ? ? ? ?  77.352 ± ?  1.339 ? ms/op
FileSplitterBenchmark.splitFileZeroCopy:·gc.alloc.rate ? ? ? avgt ? 15 ? ? ? ?  23.759 ± ?  0.465  MB/sec
FileSplitterBenchmark.splitFileZeroCopy:·gc.alloc.rate.norm  avgt ? 15 ? ? 2555608.877 ± 8644.153 ?  B/op
FileSplitterBenchmark.splitFileZeroCopy:·gc.count ? ? ? ? ?  avgt ? 15 ? ? ? ?  10.000 ? ? ? ? ? ? counts
FileSplitterBenchmark.splitFileZeroCopy:·gc.time ? ? ? ? ? ? avgt ? 15 ? ? ? ? ? 5.000 ? ? ? ? ? ? ? ? ms

以下是用于上述結果的基準測試代碼和文件大小。

int maxSizePerFileInBytes = 1024 * 1024 // 1 MB chunks
?
public void setup() throws Exception {inputFile = Paths.get("/tmp/large_input.txt");outputDir = Paths.get("/tmp/split_output");// Create a large file for benchmarking if it doesn't existif (!Files.exists(inputFile)) {try (BufferedWriter writer = Files.newBufferedWriter(inputFile)) {for (int i = 0; i < 10_000_000; i++) {writer.write("This is line number " + i);writer.newLine();}}}
}
?
public void splitFile() throws Exception {splitter.split(inputFile, outputDir);
}
?
public void splitFileZeroCopy() throws Exception {zeroCopySplitter.split(inputFile);
}

zeroCopy表現出相當大的加速,僅用了 77 毫秒,而對于這種特定情況,【常用方式】需要 1179 毫秒。在處理大量數據或許多文件時,這種性能優勢可能至關重要。

結論

高效拆分大型文本文件需要系統級性能考慮,而不僅僅是邏輯。雖然基本方法突出了內存作過多的問題,但重新設計的解決方案利用零拷貝技術并保持行完整性,可以顯著提高性能。

這證明了系統感知編程和理解 I/O 機制在創建更快、更節省資源的工具來處理大型文本數據(如日志或數據集)方面的影響。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/82707.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/82707.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/82707.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

打破產品思維--被討厭的勇氣--實戰5

課程&#xff1a;B站大學 記錄產品經理實戰項目系統性學習&#xff0c;從產品思維&#xff0c;用戶畫像&#xff0c;用戶體驗&#xff0c;增長數據驅動等不同方向理解產品&#xff0c;從0到1去理解產品從需求到落地的全過程&#xff0c;測試左移方向&#xff08;靠近需求、設計…

【Autosar SecOC 1.信息安全原理介紹】

這里寫目錄標題 1 背景2 了解黑客攻擊原理3 SecOC實現數據的真實性與完整性校驗3.1 數據身份驗證完成真實性驗證3.2 防止重放攻擊 1 背景 在今天的車載網絡中&#xff0c;大部分數據傳輸是在沒有任何特殊安全措施的情況下進行的。因此&#xff0c;一旦能夠直接訪問車輛的總線&a…

基于SpringBoot的校園周邊美食探索及分享平臺【附源碼+數據庫+文檔下載】

一、項目簡介 本項目是一個基于 SpringBoot Vue 的校園周邊美食探索與分享平臺&#xff0c;專為在校大學生開發&#xff0c;集美食推薦、好友互動、收藏分享于一體。 通過平臺&#xff0c;用戶可以探索學校周邊的美食店鋪、發布美食鑒賞、添加好友進行交流分享。同時&#x…

無償幫寫畢業論文

以下教程教你如何利用相關網站和AI免費幫你寫一個畢業論文。畢竟畢業論文只要過就行&#xff0c;脫產學習這么多年&#xff0c;終于熬出頭了&#xff0c;完成畢設后有空就去多看看親人好友&#xff0c;祝好&#xff01; 一、找一個論文模板(最好是overleaf) 廢話不多說&#…

15 個 Azure DevOps 場景化面試問題及解答

問題 1. 解釋 Azure DevOps YAML 管道的典型結構。 您可以從管道的整體結構開始&#xff0c;從觸發器開始。您也可以選擇解釋它可能包含的不同類型的階段&#xff1a;構建、測試、掃描、部署等。 Azure DevOps YAML 管道結構示例 觸發器指示管道運行。它可以是持續集成 (CI) 或…

Java 大視界 -- Java 大數據機器學習模型在元宇宙虛擬場景智能交互中的關鍵技術(239)

&#x1f496;親愛的朋友們&#xff0c;熱烈歡迎來到 青云交的博客&#xff01;能與諸位在此相逢&#xff0c;我倍感榮幸。在這飛速更迭的時代&#xff0c;我們都渴望一方心靈凈土&#xff0c;而 我的博客 正是這樣溫暖的所在。這里為你呈上趣味與實用兼具的知識&#xff0c;也…

本地不安裝oracle,還想連oracle

1.首先要用navicat,或者toad打開連接數據庫 2.安裝oracle客戶端&#xff0c;有時候OCI.dll需要看數據庫版本&#xff0c;我們Oracle數據庫是12C&#xff0c;可以用這個版本 3. 4.配置環境變量 變量名&#xff1a;NLS_LANG變量值&#xff1a;SIMPLIFIED CHINESE_CHINA.ZHS16GBK …

LabVIEW車牌自動識別系統

在智能交通快速發展的時代&#xff0c;車牌自動識別系統成為提升交通管理效率的關鍵技術。本案例詳細介紹了基于 LabVIEW 平臺&#xff0c;搭配大恒品牌相機構建的車牌自動識別系統&#xff0c;該系統在多個場景中發揮著重要作用&#xff0c;為交通管理提供了高效、精準的解決方…

deque底層數據結構以及和queue的異同

文章目錄 底層數據結構原理關鍵組成部分操作效率與其他容器的對比適用場景C STL中的實現細節總結 deque和queue的異同相同點不同點 deque&#xff08;雙端隊列&#xff09;是一種具有高效兩端插入和刪除操作的數據結構&#xff0c;常見于C標準庫&#xff08;STL&#xff09;和其…

WordPress 網站上的 jpg、png 和 WebP 圖片插件

核心功能 1. 轉換 AVIF 并壓縮 AVIF 將您 WordPress 網站上的 jpg、png 和 WebP 圖片轉換為 AVIF 格式&#xff0c;并根據您設置的壓縮級別壓縮 AVIF 圖片。如果原始圖片已經是 WordPress 6.5 以上支持的 AVIF 格式&#xff0c;則原始 AVIF 圖片將僅被壓縮。 2. 轉換 WebP 并…

Docker Volumes

Docker Volumes 是 Docker 提供的一種機制&#xff0c;用于持久化存儲容器數據。與容器的生命周期不同&#xff0c;Volumes 可以獨立存在&#xff0c;即使容器被刪除&#xff0c;數據仍然保留。以下是關于 Docker Volumes 的詳細說明&#xff1a; 1. 為什么需要 Volumes&#…

西電 | 2025年擬錄取研究生個人檔案錄取通知書郵寄通知

各位考生&#xff1a; 我校2025年碩士研究生錄取工作已結束&#xff0c;根據相關工作管理規定&#xff0c;現將個人檔案轉調及錄取通知書郵寄信息確認等有關事宜通知如下&#xff1a; 一、個人檔案轉調 &#xff08;郵寄檔案請務必使用EMS&#xff09; 1.全日制考生 錄取類…

ExcelJS庫的使用

ExcelJS 安裝 npm install exceljs新的功能! Merged fix: styles rendering in case when “numFmt” is present in conditional formatting rules (resolves #1814) #1815. Many thanks to andreykrupskii for this contribution!Merged inlineStr cell type support #15…

時空注意力機制深度解析:理論、技術與應用全景

時空注意力機制作為深度學習領域的關鍵技術&#xff0c;通過捕捉數據在時間和空間維度上的依賴關系&#xff0c;顯著提升了時序數據處理和時空建模能力。本文從理論起源、數學建模、網絡架構、工程實現到行業應用&#xff0c;系統拆解時空注意力機制的核心原理&#xff0c;涵蓋…

wxWidgets 3.2.8 發布,修復了GTK下,wxStaticText顯示文本異常的問題

詳細如下&#xff1a; 3.2.8 是穩定的 3.2 系列中的最新維護版本&#xff0c;現已在 GitHub 上提供&#xff0c;您可以從中下載帶有 所選 Windows 的庫源和文檔以及二進制文件 編譯器&#xff0c;例如 Microsoft Visual C、MinGW-w64 和 TDM-GCC。您還可以閱讀更新的文檔 版本&…

網頁Web端無人機直播RTSP視頻流,無需服務器轉碼,延遲300毫秒

隨著無人機技術的飛速發展&#xff0c;全球無人機直播應用市場也快速擴張&#xff0c;從農業植保巡檢到應急救援指揮&#xff0c;從大型活動直播到智慧城市安防&#xff0c;實時視頻傳輸已成為剛需。預計到2025年&#xff0c;全球將有超過1000萬架商用無人機搭載直播功能&#…

思維鏈框架:LLMChain,OpenAI,PromptTemplate

什么是思維鏈,怎么實現 目錄 什么是思維鏈,怎么實現思維鏈(Chain of Thought)在代碼中的實現方式1. 手動構建思維鏈提示2. 少樣本思維鏈提示3. 自動思維鏈生成4. 思維鏈與工具使用結合5. 使用現有思維鏈框架:LLMChain,OpenAI,PromptTemplate思維鏈實現的關鍵要點思維鏈(C…

杰理強制燒錄撥碼開關

5.3. 工具撥碼開關說明 — JL Project Documentation

智能手表關鍵技術評估報告

?? 智能手表關鍵技術評估報告 產品名稱:Aurora Watch S1 智能手表 編寫日期:2025年5月6日 版本號:v1.0 編寫人:XXX(技術負責人) 一、報告目的 本報告旨在對智能手表核心技術模塊進行全面評估,識別項目研發過程中可能存在的技術風險、供應鏈瓶頸和開發難點,并為架構…

基于RT-Thread驅動EEPROM_AD24C02

基于RT-Thread驅動EEPROM_AD24C02 前言一、硬件設計二、軟件設計三、測試1、eeprom_test&#xff08;&#xff09;測試2、基礎操作字節實驗3、多字節讀寫 前言 存儲容量2048位&#xff0c;內部組織256x8&#xff08;2K&#xff09;&#xff0c;即256個字節的存儲單元&#xff…