簡體   English   中英

使用 csv-parse 和 node 在讀取和解析文件中添加快速隨機訪問

[英]Adding fast random access in reading and parsing file using csv-parse and node

我正在使用csv-parser庫來處理節點中的 csv 解析。 該文件可能很大,從 50,000 到 500,000 行不等,甚至可能更大。 我必須在 csv 上執行一些計算,然后將其提交給服務器,因為我正在考慮將 csv 分成塊,然后我可以提供給工作線程以執行計算。 工作線程將獲取要跳過的行數,然后在達到特定限制之后開始讀取行數。 我創建了一個讀取 stream 並傳遞 csv-parser 並選擇要跳過的行數。 我試圖對其進行一些基准測試,但在跳線和不跳線之間沒有發現明顯的好處。 即使我閱讀了整個文件,有時也比閱讀結尾的 30,000 行要快。

我的猜測是,這個問題是因為讀取 stream 會一一讀取數據,因此對於文件的快速隨機訪問來說並不完美。

也許我的基准測試是錯誤的?

這是一段代碼

const csv = require('csv-parser');
const bench = require('nanobench');

const fs = require('fs');
const parse = (number) => {
// const results = [];
    fs.createReadStream('test.csv').pipe(csv({
        skipLines: number
    })).on('data', (data) => {}).on('end', () => {});
}

const arr = [0, 30000, 15000, 15000/2, 15000/4, 15000/8];

arr.forEach(item => {
    bench(`CSV skip ${item} lines 40 times`, function(b) {
        b.start();
        for(let i = 0; i < 40; i++) parse(item);
        b.end();
    })
})

這是輸出

# CSV skip 0 lines 40 times
ok ~4.14 ms (0 s + 4139981 ns)

# CSV skip 30000 lines 40 times
ok ~2.05 ms (0 s + 2054537 ns)

# CSV skip 15000 lines 40 times
ok ~2.7 ms (0 s + 2702328 ns)

# CSV skip 7500 lines 40 times
ok ~2.43 ms (0 s + 2434555 ns)

# CSV skip 3750 lines 40 times
ok ~1.97 ms (0 s + 1966652 ns)

# CSV skip 1875 lines 40 times
ok ~2.17 ms (0 s + 2172144 ns)

對於我的目標,還有其他更好的方法嗎?

問題是,即使你想跳過 N 行,解析器仍然必須讀取和分析從上到下到第 N 行的所有字節。 第一行離開頭越遠,執行的無用工作就越多( Schlemiel the Painter's algorithm )。

您可以考慮以下邏輯:

  • 對於每個文件,從currentPosition = 0開始
  • 尋找偏移量currentPosition + chunkSize
  • 讀取i個字節,直到遇到換行符或 EOF
  • 使用參數position=currentPositionsize = chunkSize + i分配一個新線程
  • 繼續currentPosition = currentPosition + size + 1

這樣,每個塊將包含整數行。

在一個線程中,使用參數positionsize讀取整個塊並在內存中解析它。

在偽代碼中:

size = fs.statSync("filename").size

chunkSize = 99999

currentPos = 0

fd = fs.open("filename")

while (currentPos < size) {

    endPos = currentPos + chunkSize
    fs.readSync(fd, buf, 0, 1000, endPos)
    
    i = 0
    while(buf[i] != \n) i++
    endPos += i
    
    threads.add(filename: "filename", position: currentPos, size: endPos - currentPos)

    currentPos = endPos + 1
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM