[simdjson] document_stream | iterate_many() | batch_size | 線程加速 | 輕量handle

第七章:文檔流

歡迎回來

在前面的章節中,我們學習了如何使用解析器結合填充字符串獲取表示JSON根節點的文檔,并通過按需API(On-Demand API)遍歷值、對象和數組,同時使用simdjson_result進行錯誤處理。

到目前為止,我們專注于逐個解析單個JSON文檔。

但如果需要處理包含多個連續JSON文檔的大型文件或網絡流呢?

常見格式如NDJSON(換行符分隔的JSON)或JSON Lines,其中每行都是完整且有效的JSON文檔。

例如:

{"user":"Alice", "id":1}
{"user":"Bob", "id":2}
{"user":"Charlie", "id":3}
...(可能有數百萬行)

將整個文件加載到內存逐個解析會非常低效且消耗大量內存。這正是文檔流抽象要解決的問題。

什么是文檔流?

simdjson::ondemand::document_stream(DOM API對應simdjson::dom::document_stream,但我們聚焦按需API的iterate_many)旨在高效處理包含多個JSON文檔的大型輸入。

  • 不同于逐個提供文檔字符串,iterate_many(DOM API用parse_many一次性接收整個緩沖區或文件內容。

  • iterate_many不會立即解析所有內容,而是建立內部機制,在迭代流時逐個查找并解析文檔。

想象成傳送帶:將整個大型容器裝載到傳送帶(iterate_many),然后逐個處理到達面前的物品(循環遍歷document_stream)。無需同時存儲所有物品,只需處理當前項。

這種方法有兩大優勢:

  1. 內存高效:無需同時加載整個GB級流到內存(盡管需要初始大緩沖區)。解析器為每個文檔復用內部緩沖區。
  2. 高性能:Simdjson可用高速方法處理流塊。按需API甚至能用后臺線程在您處理當前文檔時查找下一個文檔!

獲取文檔流

通過simdjson::ondemand::parser實例的iterate_many()方法獲取document_stream

鏈接:https://github.com/simdjson/simdjson/blob/master/doc/iterate_many.md

iterate_many()參數通常包括:

  1. 填充JSON數據緩沖區指針(const uint8_t*padded_string
  2. 數據長度(size_t
  3. 可選的batch_size(后續討論)
  4. 可選的允許逗號分隔文檔標志(NDJSON/JSONL不常用)

與其他simdjson操作類似,iterate_many()返回simdjson_result<simdjson::ondemand::document_stream>。使用流前必須檢查結果是否有錯誤。

以下為示例NDJSON數據處理:

#include <simdjson.h>
#include <iostream>int main() {// 假設從大文件讀取的NDJSON數據simdjson::padded_string ndjson_data = R"({"user":"Alice", "id":1}{"user":"Bob", "id":2}{"user":"Charlie", "id":3})"_padded; // _padded確保填充simdjson::ondemand::parser parser;// 獲取文檔流simdjson::simdjson_result<simdjson::ondemand::document_stream> stream_result =parser.iterate_many(ndjson_data);// 檢查流設置是否成功if (stream_result.error()) {std::cerr << "文檔流設置錯誤: " << stream_result.error() << std::endl;return EXIT_FAILURE;}// 從結果中獲取document_stream對象simdjson::ondemand::document_stream doc_stream = std::move(stream_result.value());std::cout << "成功建立文檔流。" << std::endl;// 現在可以迭代流...(見下節)return EXIT_SUCCESS;
}

注意iterate_many接收包含所有JSON文檔的整個填充緩沖區。

迭代文檔流

simdjson::ondemand::document_stream設計用于基于范圍的for循環。循環中每個項是simdjson_result<simdjson::ondemand::document_reference>

為什么用document_reference而非document

因為流為高效性復用解析器內部單個document對象。document_reference輕量句柄,指向此內部可復用文檔對象。每次循環移動時,內部文檔對象更新為流中下一個JSON文檔。

#include <simdjson.h>
#include <iostream>int main() {simdjson::padded_string ndjson_data = R"({"user":"Alice", "id":1}{"user":"Bob", "id":2}{"user":"Charlie", "id":3})"_padded;simdjson::ondemand::parser parser;auto stream_result = parser.iterate_many(ndjson_data);if (stream_result.error()) {std::cerr << "文檔流設置錯誤: " << stream_result.error() << std::endl;return EXIT_FAILURE;}simdjson::ondemand::document_stream doc_stream = std::move(stream_result.value());// 遍歷流中每個文檔int doc_count = 0;for (auto doc_result : doc_stream) {// 每個'doc_result'是simdjson_result<simdjson::ondemand::document_reference>// 訪問文檔前檢查錯誤if (doc_result.error()) {std::cerr << "解析文檔" << doc_count << "錯誤: " << doc_result.error() << std::endl;// 可選擇繼續或停止continue; // 跳過損壞文檔}// 獲取文檔引用simdjson::ondemand::document_reference doc = doc_result.value();std::cout << "處理文檔 " << doc_count << ":" << std::endl;// 可像常規文檔一樣使用'doc'對象訪問字段auto user_result = doc["user"].get_string();if (!user_result.error()) {std::cout << "  用戶: " << user_result.value() << std::endl;} else {std::cerr << "  獲取用戶字段錯誤: " << user_result.error() << std::endl;}auto id_result = doc["id"].get_int64();if (!id_result.error()) {std::cout << "  ID: " << id_result.value() << std::endl;} else {std::cerr << "  獲取ID字段錯誤: " << id_result.error() << std::endl;}doc_count++;}std::cout << "已完成處理 " << doc_count << " 個文檔。" << std::endl;// 'parser'和'ndjson_data'在循環期間需保持有效return EXIT_SUCCESS;
}

輸出:

在這里插入圖片描述

此模式是使用document_stream的核心。遍歷流時獲取當前JSON文檔句柄(document_reference),然后用標準按需API方法(get_object()[]get_string()等)處理。

錯誤處理至關重要。錯誤可能發生在流設置(stream_result.error())或循環內解析單個文檔時(doc_result.error()),均需檢查。

批處理大小與內存

調用iterate_many(buffer, len, batch_size)時,batch_size參數很重要。它定義simdjson初始結構分析(階段1)時處理的輸入緩沖區塊大小。

  • batch_size需大于流中最大單個JSON文檔。若文檔大于批處理大小,simdjson無法在單批次正確解析。
  • 更大batch_size可能通過減少批次切換開銷提升性能,但需解析器分配足夠內存。
  • 合理默認值通常為1MB,但可根據文檔大小調整。解析器容量需通過parser.allocate()設置足夠大或自動擴展。

在這里插入圖片描述

Simdjson讀取batch_size字節(或剩余字節),在階段1查找塊內所有文檔邊界,迭代時從該塊提供文檔。消耗完塊內文檔后加載下一批次

線程加速

iterate_many的強大功能之一(當simdjson啟用線程時)是使用后臺線程重疊工作。

主線程處理當前批次文檔時(循環調用document_reference方法),工作線程可同時在后臺對下一批次數據執行階段1分析。

在這里插入圖片描述

這種重疊意味著后續批次階段1的時間被"隱藏"在當前批次處理時間內。若單文檔處理時間足夠長,下批次階段1可能已完成,使階段1成本趨近于零!

(類似于 生產消費者模型的思想)

無需修改循環代碼即可啟用此功能。若simdjson編譯時啟用線程(SIMDJSON_THREADS_ENABLED),iterate_many自動在適當時使用后臺線程。

底層機制(簡化版)

document_stream對象管理輸入緩沖區batch_size及跨文檔和批次的迭代狀態。包含:

  • 原始輸入緩沖區指針及長度
  • simdjson::ondemand::parser指針
  • 當前處理批次信息(起始位置batch_start
  • 跟蹤批次內當前文檔的內部狀態
  • 啟用線程時管理stage1_worker線程對象和輔助parser實例(stage1_thread_parser

調用iterate_many(buffer, len, batch_size)時:

  1. 構建document_stream對象,存儲緩沖區/長度/批次大小和解析器指針
  2. 啟動循環(stream.begin())觸發初始處理:
    • 主解析器內部緩沖區可能調整以適應batch_size
    • 用主解析器對輸入緩沖區第一個batch_size字節執行階段1
    • 設置初始document句柄指向第一個文檔
    • 啟用線程且有更多數據時,啟動工作線程對下一批次執行階段1
  3. 循環內(for (auto doc_result : doc_stream)document_reference包裝主解析器內部文檔狀態。對doc的操作(get_object()[]等)執行當前文檔階段2解析
  4. 循環++運算符移動至下一文檔時:
    • 主解析器內部迭代器跳過當前文檔
    • 若迭代器到達當前批次末尾,檢查工作線程是否完成下一批次階段1
    • 若下一批次階段1就緒,快速交換主解析器和工作解析器內部狀態。工作解析器成為新主解析器
    • 禁用線程或工作線程未就緒時,主線程自行執行下一批次階段1
    • 加載新批次后更新內部文檔句柄指向首文檔
    • 無更多批次時結束流迭代

關鍵在于document_stream管理緩沖區塊和后臺階段1處理,通過迭代器接口逐個產出文檔。

高級:位置與截斷

如需更多控制或調試,document_stream::iterator(范圍for循環隱式使用)提供額外方法:

  • iterator::current_index() const返回當前文檔在原始緩沖區的字節偏移,用于記錄錯誤或跟蹤大文件進度
  • iterator::source() const:返回指向當前文檔原始字節的std::string_view(可能含周圍空白)
  • iterator::error() const:返回當前文檔error_code

流迭代完成后,document_stream對象提供:

  • document_stream::truncated_bytes() const:返回緩沖區末尾無法解析為完整JSON文檔的字節數,用于檢測不完整輸入或尾部垃圾

逗號分隔文檔

默認iterate_many期望JSON空白分隔文檔。

若文檔用逗號分隔(如1, "hello", true, {}),可在iterate_many傳入allow_comma_separated參數為true。但此模式需整個輸入作為單批次處理(忽略batch_size)且禁用線程,不適合大規模流式處理。

總結

處理多JSON文檔大型文件(如NDJSON/JSON Lines)需內存高效方案。simdjson::ondemand::document_stream專為此設計:

  • 通過parser.iterate_many(padded_data, ...)獲取文檔流
  • 用范圍for循環迭代流:for (auto doc_result : stream)
  • 循環項是表示流中文檔的simdjson_result<document_reference>,訪問前需檢查錯誤
  • batch_size參數控制階段1處理塊大小,應不小于最大文檔
  • 啟用線程時,iterate_many可重疊下批次階段1與當前批次處理,極大提升吞吐
  • 循環中document_reference是輕量句柄,指向解析器復用內存

(通過指針移動,減少拷貝開銷和零碎內存)

使用document_stream可高效解析多文檔輸入,使simdjson成為處理流式JSON數據的利器。

掌握解析、數據處理、導航、錯誤處理和流處理后,我們已建立堅實基礎。下一章將揭秘simdjson通過實現/CPU派發機制實現高性能的奧秘。

下一章:實現/CPU派發


補充:

輕量句柄

輕量句柄是內存中對象的簡易引用,不直接存儲數據,僅指向已存在的資源(如解析后的文檔)

循環中的document_reference作為輕量句柄,避免了重復解析,直接復用內存中的結果,節省計算開銷。

類似書簽標記書頁位置,書簽(輕量句柄)本身不是書頁內容,但能快速定位到已存在的頁面(內存數據)。

核心特點

  • 低開銷:不復制數據,僅保存指向內存的指針或標識符。
  • 高效復用:通過引用共享已解析內容,避免重復處理。
  • 無所有權:輕量句柄不管理資源生命周期,需確保目標資源有效。

(通過指針移動,減少拷貝開銷和零碎內存)

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

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

相關文章

【機器學習】向量數據庫選型指南:企業內網部署場景

向量數據庫選型指南&#xff1a;企業內網部署場景一、選型背景與關鍵需求 在企業級機器學習應用中&#xff0c;特別是涉及圖片、視頻等非結構化數據的場景&#xff0c;向量數據庫已成為核心基礎設施。傳統數據庫難以高效處理高維向量的相似度檢索需求&#xff08;如圖片相似性搜…

Django母嬰商城項目實踐(八)- 數據渲染與顯示之首頁

8、數據渲染與顯示 1 概述 Django作為Web框架,需要一種很便利的方法動態地生成HTML網頁,因此有了模板這個概念。模板包含所需HTML的部分代碼以及一些特殊語法,特殊語法用于描述如何將視圖傳遞的數據動態插入HTML網頁中。 Django可以配置一個或多個模板引擎(甚至是0個,如前…

Redis常見線上問題

文章目錄 Redis常見線上問題 引言 報告背景與目的 Redis版本與環境說明 性能瓶頸問題 慢查詢分析與優化 高CPU與網絡延遲 內存管理問題 內存碎片成因與優化 BigKey與內存溢出 數據一致性與高可用問題 主從同步延遲 腦裂問題與解決方案 持久化機制問題 RDB與AOF對比 核心特性對比…

Typecho博客集成阿里云CDN+OSS實現全站加速方案

文章目錄 Typecho博客系統集成阿里云CDN和OSS實現靜態資源加速 引言 一、技術選型與準備工作 1.1 為什么選擇阿里云CDN+OSS組合 1.2 準備工作 二、OSS存儲桶創建與配置 2.1 創建OSS存儲桶 2.2 配置Bucket權限 2.3 配置跨域訪問(CORS) 三、CDN加速配置 3.1 添加CDN域名 3.2 配置…

計算機畢業設計Java網咖管理系統 Java技術實現的網咖綜合管理系統開發 基于Spring Boot框架的網咖運營管理系統設計

計算機畢業設計Java網咖管理系統e0btvq7l &#xff08;配套有源碼 程序 mysql數據庫 論文&#xff09;本套源碼可以先看具體功能演示視頻領取&#xff0c;文末有聯xi 可分享隨著互聯網技術的飛速發展和電子競技的全球興起&#xff0c;網咖作為一種新興的休閑娛樂場所&#xff0…

Kotlin main函數

main() 函數 來仔細看看 main() 函數。實際上&#xff0c;它就是一個很常見的函數&#xff1a;你可以對它做任何你能對普通函數做的事。唯一的不同是&#xff1a;它是程序的入口點&#xff08;entry point&#xff09;。這意味著程序的執行從調用這個函數開始。 我們來拆解一下…

深入理解 Spring:事務管理與事件機制全解析

文章目錄前言一、Spring 事務管理&#xff08;Transaction Management&#xff09;1. 使用 Transactional 管理事務2. 核心屬性說明3. 事務傳播行為詳解&#xff08;Propagation&#xff09;4. 異常回滾策略分析5. 底層原理剖析&#xff08;源碼級&#xff09;二、Spring 事件機…

AWD練習的平臺搭建

ubuntu虛擬機搭建 前提資源準備 進行AWD我們需要在一個獨立的虛擬機 現在就來搭建一個ubuntu的 這里我們使用的VMware是17的 然后下載鏡像的地址&#xff1a;Ubuntu最全的國內鏡像下載地址 - 嗶哩嗶哩 我下載的是中科大的 這里需要準備的前提資源就有了。 創建Ubuntu虛…

C++ 詳談繼承體系下的構造函數和析構函數

前言 前面呢, 我們說了C中實現多態的原理, 其中也說了, 虛函數表和虛函數指針的創建時機, C 詳談多態實現原理-CSDN博客 , 這一節呢, 我們會說說在C中繼承體系下的另一個知識點, 那就是: 繼承體系下的構造函數和析構函數~~, 主要圍繞兩個問題: 執行順序? 虛析構函數的作用? …

PostgreSQL 字段類型速查與 Java 枚舉映射

1. 查詢 SQLSELECTc.table_schema,c.table_name,c.column_name,c.data_type,c.udt_name,CASE-- 數值WHEN c.udt_name IN (int2,int4,int8,float4,float8,numeric,money)THEN NUMERIC-- 布爾WHEN c.udt_name boolTHEN BOOLEAN-- 日期/時間WHEN c.udt_name IN (date,time,timetz…

數據分析綜合應用 30分鐘精通計劃

?? 數據分析綜合應用 30分鐘精通計劃(完整版含輸出) ? 時間分配 5分鐘:數據加載與清洗基礎 10分鐘:探索性數據分析(EDA) 10分鐘:數據分析實戰案例 5分鐘:分析報告生成 ?? 第一部分:數據加載與清洗基礎 (5分鐘) 1. 模擬真實數據集 import pandas as pd import nu…

Python爬蟲實戰:研究psd-tools庫相關技術

一、引言 1.1 研究背景 Adobe Photoshop 是目前最流行的圖像處理軟件之一,其原生文件格式 PSD(Photoshop Document)包含了豐富的圖像信息和編輯歷史。PSD 文件不僅在設計領域廣泛使用,還在數字營銷、版權保護和安全分析等領域具有重要價值。然而,手動分析大量 PSD 文件是…

基于卷積傅里葉分析網絡 (CFAN)的心電圖分類的統一時頻方法

一、研究背景與核心問題??ECG分類的挑戰?&#xff1a;心電圖&#xff08;ECG&#xff09;信號分類在心律失常檢測、身份識別等領域至關重要&#xff0c;但傳統方法難以同時有效整合時域和頻域信息。現有方法包括&#xff1a;?時域分類&#xff08;CNN1D&#xff09;??&am…

Linux——LinuxOS

cd,pwd,mkdir,rm,ls,touch,cat,echo,

深度學習篇---矩陣

在機械臂解算、深度學習網絡等硬件和軟件領域中&#xff0c;矩陣運算作為核心數學工具&#xff0c;承擔著數據表示、變換、映射和優化的關鍵作用。以下從具體領域出發&#xff0c;詳細總結涉及的矩陣運算及對應的核心知識&#xff1a;一、機械臂解算領域機械臂解算&#xff08;…

元宇宙:技術烏托邦與數字化未來——基于技術哲學的分析

一、技術哲學視域下的元宇宙本質哲學源流與技術基因的雙重映射理想世界的千年回響&#xff1a;從柏拉圖洞穴隱喻中的影子世界&#xff0c;到普特南“缽中之腦”對虛擬與現實界限的消弭&#xff0c;元宇宙的構想深植于人類對平行世界的永恒追問。中國傳統神話中“天人二元結構”…

如何構建一個基于大模型的實時對話3D數字人?

近年來&#xff0c;隨著元宇宙和AIGC技術的爆發&#xff0c;3D數字人從影視特效走向日常應用。無論是虛擬主播、AI客服&#xff0c;還是數字教師&#xff0c;其核心訴求都是**“能聽、會說、有表情”**的實時交互能力。本文就帶大家了解如何構建一個基于大模型的實時對話的3D數…

NULL值處理:索引優化與業務設計實踐指南

一、NULL值的本質與影響NULL值在數據庫中代表"未知狀態"或"不適用"的特殊標記&#xff0c;與空字符串或0有本質區別12。其特性導致以下業務與性能問題&#xff1a;?語義復雜性?&#xff1a;NULL可能表示"未填寫"(如用戶手機號)或"不適用&…

【add vs commit】Git 中的 add 和 commit 之間的區別

關于git add和git commit還有一些有點不太清楚的地方&#xff0c;這里寫一篇文章好好理一理git add&#xff1a;添加到暫存區 git add實際上是把工作區中的內容存入“暫存區” 通俗來講就是告訴Git&#xff1a;“這些文件我準備好commit了” git add file.txt # 添加單個文件 …

【推薦100個unity插件】使用C#或者unity實現爬蟲爬取靜態網頁數據——Html Agility Pack (HAP)庫和XPath 語法的使用

文章目錄前言一、安裝HtmlAgilityPack1、從NuGet下載HtmlAgilityPack包2、獲取HtmlAgilityPack.dll二、HtmlAgilityPack常用操作1、加載 HTML2、查詢方式2.1 使用 XPath 查詢&#xff08;推薦&#xff09;2.2 使用 LINQ 查詢3、常用查詢操作3.1 選擇節點3.2 獲取屬性值3.3 遍歷…