0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀(guān)看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

更快的tsv解析代碼分享

jf_wN0SrCdH ? 來(lái)源:Rust語(yǔ)言中文社區(qū) ? 2023-12-29 09:45 ? 次閱讀

最近在B站沖浪時(shí)發(fā)現(xiàn)一個(gè) Rust 和 Go 解析 tsv 文件的視頻, 作者需要解析使用get-NetTCPConnection | Format-Table -Property LocalAddress,LocalPort,RemoteAddress,RemotePort,State,OwningProcess獲取的本地所有 TCP 連接信息, 文件輸出大致如下

LocalAddressLocalPortRemoteAddressRemotePortStateOwningProcess
--------------------------------------------------------------
192.168.1.454339104.210.1.98443Established4504

視頻作者使用 regex 正則庫(kù)處理輸出, 發(fā)現(xiàn)比 Go 版本慢, 優(yōu)化后雖然比 Go 快, 但并沒(méi)有領(lǐng)先多少, 于是我自己嘗試使用別的優(yōu)化方法, 解析耗時(shí)能優(yōu)化使用正則解析的 10% 左右. 下面來(lái)看看我的優(yōu)化過(guò)程.

?更快的 tsv 解析[1]

?項(xiàng)目搭建[2]

?regex 解析[3]

?減少內(nèi)存分配[4]

?使用 ascii 正則[5]

?拋棄 regex[6]

?手寫(xiě)解析狀態(tài)機(jī)[7]

?SIMD 加速?[8]

?總結(jié)[9]

項(xiàng)目搭建

進(jìn)行性能時(shí)建議使用criterion[10], 它幫我們解決了性能的內(nèi)存預(yù)加載, 操作耗時(shí), 性能記錄, 圖表輸出等功能.

cargonew--libtsv
cdtsv
cargoaddcriterion--dev-Fhtml_reports
cargoaddregex

然后在 Cargo.toml 里添加如下bench 文件

[[bench]]
name="parse"
harness=false
//benches/parse.rs
#![allow(dead_code)]
usecriterion::{black_box,criterion_group,criterion_main,Criterion};

constOUTPUT:&str=include_str!("net.tsv");

fncriterion_benchmark(c:&mutCriterion){
todo!()
}

criterion_group!(benches,criterion_benchmark);
criterion_main!(benches);

測(cè)試使用的 tsv 一共 380 行.

regex 解析

使用正則解析的正則表達(dá)式很簡(jiǎn)單, 這里直接給代碼, 為了避免重復(fù)編譯正則表達(dá)式和重新分配內(nèi)存報(bào)錯(cuò)結(jié)果列表, 這里將她們作為參數(shù)傳給解析函數(shù).

structOwnedRecord{
local_addr:String,
local_port:u16,
remote_addr:String,
remote_port:u16,
state:String,
pid:u64,
}
fnregex_owned(input:&str,re:®ex::Regex,result:&mutVec){
input.lines().for_each(|line|{
ifletSome(item)=re.captures(line).and_then(|captures|{
let(_,[local_addr,local_port,remote_addr,remote_port,state,pid])=
captures.extract();
letret=OwnedRecord{
local_addr:local_addr.to_string(),
local_port:local_port.parse().ok()?,
remote_addr:remote_addr.to_string(),
remote_port:remote_port.parse().ok()?,
state:state.to_string(),
pid:pid.parse().ok()?,
};
Some(ret)
}){
result.push(item);
}
});
assert_eq!(result.len(),377);
}

parse.rs 文件里要加上使用的正則和提前創(chuàng)建好列表, 并且將函數(shù)添加的 bench 目標(biāo)里

fncriterion_benchmark(c:&mutCriterion){
letre=regex::new(r"(S+)s+(d+)s+(S+)s+(d+)s+(S+)s+(d+)").unwrap();
letmutr1=Vec::with_capacity(400);
c.bench_function("regex_owned",|b|{
b.iter(||{
//重置輸出vector
r1.clear();
regex_owned(black_box(OUTPUT),&re,&mutr1);
})
});
}

接著跑cargo bench --bench parse進(jìn)行測(cè)試, 在我的電腦上測(cè)得每次運(yùn)行耗時(shí) 450 μs 左右.

減少內(nèi)存分配

一個(gè)最簡(jiǎn)單的優(yōu)化是使用&str以減少每次創(chuàng)建String帶來(lái)的內(nèi)存分配和數(shù)據(jù)復(fù)制.

structRecord<'a>{
local_addr:&'astr,
local_port:u16,
remote_addr:&'astr,
remote_port:u16,
state:&'astr,
pid:u64,
}

兩個(gè)函數(shù)代碼差不多, 所以這里不再列出來(lái), 可以通過(guò)gits: tsv 解析[11]獲取完整代碼.

可惜這次改動(dòng)帶來(lái)的優(yōu)化非常小, 在我的電腦上反復(fù)測(cè)量, 這個(gè)版本耗時(shí)在 440 μs 左右.

使用 ascii 正則

rust 的 regex 正則默認(rèn)使用 unicode, 相比于 ascii 編碼, unicode 更復(fù)雜, 因此性能也相對(duì)較低, 剛好要解析的內(nèi)容都是ascii字符, 使用 ascii 正則是否能提升解析速度呢? regex 有regex::bytes模塊用于 ascii 解析, 但為了適配字段, 這里不得不使用transmute將&[u8]強(qiáng)制轉(zhuǎn)換成&str

fncast(data:&[u8])->&str{
unsafe{std::transmute(data)}
}
fnregex_ascii<'a>(input:&'astr,re:®ex::Regex,result:&mutVec>){
input.lines().for_each(|line|{
ifletSome(item)=re.captures(line.as_bytes()).and_then(|captures|{
let(_,[local_addr,local_port,remote_addr,remote_port,state,pid])=
captures.extract();
letret=Record{
local_addr:cast(local_addr),
local_port:cast(local_port).parse().ok()?,
remote_addr:cast(remote_addr),
remote_port:cast(remote_port).parse().ok()?,
state:cast(state),
pid:cast(pid).parse().ok()?,
};
Some(ret)
}){
result.push(item);
}
});
assert_eq!(result.len(),377);
}

添加到 bench 后性能大概多少呢?, 很遺憾, 性能與 regex_borrow 差不多, 在 430 μs 左右.

拋棄 regex

鑒于內(nèi)容格式比較簡(jiǎn)單, 如果只使用 rust 內(nèi)置的 split 等方法解析性能會(huì)不會(huì)更好呢? 解析思路很簡(jiǎn)單, 使用lines得到一個(gè)逐行迭代器, 然后對(duì)每行使用 split 切分空格再逐個(gè)解析即可

fnsplit<'a>(input:&'astr,result:&mutVec>){
input
.lines()
.filter_map(|line|{
letmutiter=line.split(['','	','
']).filter(|c|!c.is_empty());
letlocal_addr=iter.next()?;
letlocal_port:u16=iter.next()?.parse().ok()?;
letremote_addr=iter.next()?;
letremote_port:u16=iter.next()?.parse().ok()?;
letstate=iter.next()?;
letpid:u64=iter.next()?.parse().ok()?;
Some(Record{
local_addr,
local_port,
remote_addr,
remote_port,
state,
pid,
})
})
.for_each(|item|result.push(item));
assert_eq!(result.len(),377);
}

注意line.split只后還需要過(guò)濾不是空白的字符串, 這是因?yàn)樽址?a b"split 之后得到["a", "", "b"].

經(jīng)測(cè)試, 這個(gè)版本測(cè)試耗時(shí)大概為 53 μs, 這真是一個(gè)巨大提升, rust 的 regex 性能確實(shí)有些問(wèn)題.

每次 split 之后還需要 filter 感覺(jué)有些拖沓, 剛好有個(gè)split_whitespace[12], 換用這個(gè)方法, 將新的解析方法命名為split_whitespace后再測(cè)試下性能

letmutiter=line.split_whitespace();

令人意想不到的是性能居然倒退了, 這次耗時(shí)大概 60 μs, 仔細(xì)研究下來(lái)還是 unicode 的問(wèn)題, 改用 ascii 版本的split_ascii_whitespace之后性能提升到 45 μs.

手寫(xiě)解析狀態(tài)機(jī)

除了上述的方法, 我還嘗試將 Record 的 local_addr 和 remote_addr 改成std::IpAddr, 消除next()?.parse().ok()?等其他方法, 但收益幾乎沒(méi)有, 唯一有作用的辦法是手寫(xiě)解析狀態(tài)機(jī).

大致思路是, 對(duì)于輸出來(lái)說(shuō), 我們只關(guān)系它是以下三種情況

1.換行符 NL

2.除了換行符的空白符 WS

3.非空白字符 CH

只解析 LocalAddr 和 LocalPort 解析狀態(tài)機(jī)如下, 如果要解析更多字段, 按順序添加即可.

094b06a8-a585-11ee-8b88-92fbcf53809c.png

因?yàn)榇a有些復(fù)雜, 所以這里不再貼出來(lái), 完整代碼在 gits 上. 手寫(xiě)狀態(tài)機(jī)的版本耗時(shí)大概在 32 μs 左右. 這版本主要性能提升來(lái)自手寫(xiě)狀態(tài)機(jī)減少了循環(huán)內(nèi)的分支判斷.

SIMD 加速?

在上面手寫(xiě)解析的例子里, 處理過(guò)程類(lèi)似與將輸出作為一個(gè) vec, 狀態(tài)機(jī)作為另一個(gè) vec, 將兩個(gè) vec 進(jìn)行某種運(yùn)算后輸出結(jié)果, 應(yīng)該能使用 simd 進(jìn)行加速, 但我還沒(méi)想出高效實(shí)現(xiàn). 所以這里只給出可能的參考資料

1.zsv[13]使用 simd 加速的 csv 解析庫(kù)

2.simd base64[14]一篇介紹使用 simd 加速 base64 解析的博客, 非常推薦

總結(jié)

rust regex 在某時(shí)候確實(shí)存在性能問(wèn)題, 有時(shí)候使用簡(jiǎn)單的 split 的方法手動(dòng)解析反而更簡(jiǎn)單性能也更高, 如果情況允許, 使用 ascii 版本能進(jìn)一步提升性能, 如果你追求更好的性能, 手寫(xiě)一個(gè)狀態(tài)不失為一種選擇, 當(dāng)然我不建議在生產(chǎn)上這么做. 同時(shí)我也期待有 simd 加速的例子.

審核編輯:黃飛

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀(guān)點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • TCP
    TCP
    +關(guān)注

    關(guān)注

    8

    文章

    1375

    瀏覽量

    79162
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4344

    瀏覽量

    62813
  • 內(nèi)存分配
    +關(guān)注

    關(guān)注

    0

    文章

    16

    瀏覽量

    8313

原文標(biāo)題:更快的 tsv 解析

文章出處:【微信號(hào):Rust語(yǔ)言中文社區(qū),微信公眾號(hào):Rust語(yǔ)言中文社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    TSV工藝流程與電學(xué)特性研究

    本文報(bào)道了TSV過(guò)程的細(xì)節(jié)。還顯示了可以在8-in上均勻地形成許多小的tsv(直徑:6 m,深度:22 m)。通過(guò)這種TSV工藝的晶片。我們?nèi)A林科納研究了TSV的電學(xué)特性,結(jié)果表明
    發(fā)表于 06-16 14:02 ?3417次閱讀
    <b class='flag-5'>TSV</b>工藝流程與電學(xué)特性研究

    請(qǐng)問(wèn)有TSV6390AIDT和/或TSV6290AIDT的SPICE型號(hào)嗎?

    你有TSV6390AIDT和/或TSV6290AIDT的SPICE型號(hào)嗎? 謝謝, 何魯麗 #運(yùn)算放大器,香料宏模型
    發(fā)表于 08-06 14:07

    硅通孔(TSV)電鍍

    硅通孔(TSV)電鍍的高可靠性是高密度集成電路封裝應(yīng)用中的一個(gè)有吸引力的熱點(diǎn)。本文介紹了通過(guò)優(yōu)化濺射和電鍍條件對(duì)完全填充TSV的改進(jìn)。特別注意具有不同種子層結(jié)構(gòu)的樣品。這些樣品是通過(guò)不同的濺射和處理
    發(fā)表于 01-09 10:19

    C++的G代碼解析算法研究

    在數(shù)控技術(shù)發(fā)展過(guò)程中,G 代碼解析優(yōu)劣是促進(jìn)數(shù)控技術(shù)的發(fā)展因素之一。但目前的解析算法,并不能更高效的進(jìn)行解析處理。經(jīng)過(guò)對(duì)G 代碼進(jìn)行分析,
    發(fā)表于 07-21 16:36 ?0次下載

    軌到軌輸入/輸出20 MHz的運(yùn)算放大器TSV991/TSV992/TSV994

    The TSV99x and TSV99xA family of single, dual, and quad operational amplifiers offers low voltage
    發(fā)表于 09-04 14:51 ?12次下載
    軌到軌輸入/輸出20 MHz的運(yùn)算放大器<b class='flag-5'>TSV</b>991/<b class='flag-5'>TSV</b>992/<b class='flag-5'>TSV</b>994

    通用輸入/輸出軌到軌低功耗操作放大器TSV321/TSV358/TSV324/TSV321A/TSV358A/TSV324A

    The TSV358, TSV358A, TSV324, and TSV324A (dual and quad) devices are low voltage versions of
    發(fā)表于 09-05 09:12 ?6次下載
    通用輸入/輸出軌到軌低功耗操作放大器<b class='flag-5'>TSV</b>321/<b class='flag-5'>TSV</b>358/<b class='flag-5'>TSV</b>324/<b class='flag-5'>TSV</b>321A/<b class='flag-5'>TSV</b>358A/<b class='flag-5'>TSV</b>324A

    微(60μ一)寬的帶寬(2.4 MHz)的CMOS運(yùn)算放大器TSV6390/TSV6390A/TSV6391/TSV6391A

    The TSV6390, TSV6391, and their “A” versions are single operational amplifiers (op amps) offering
    發(fā)表于 09-05 09:34 ?4次下載
    微(60μ一)寬的帶寬(2.4 MHz)的CMOS運(yùn)算放大器<b class='flag-5'>TSV</b>6390/<b class='flag-5'>TSV</b>6390A/<b class='flag-5'>TSV</b>6391/<b class='flag-5'>TSV</b>6391A

    高功因數(shù)(1.15兆赫為45微米)cmos運(yùn)算放大器TSV521/TSV522/TSV524/TSV521A/TSV522A/TSV524A

    The TSV52x and TSV52xA series of operational amplifiers offer low voltage operation and rail-torail
    發(fā)表于 09-05 09:52 ?5次下載
    高功因數(shù)(1.15兆赫為45微米)cmos運(yùn)算放大器<b class='flag-5'>TSV</b>521/<b class='flag-5'>TSV</b>522/<b class='flag-5'>TSV</b>524/<b class='flag-5'>TSV</b>521A/<b class='flag-5'>TSV</b>522A/<b class='flag-5'>TSV</b>524A

    軌到軌輸入/輸出60μ880千赫5V CMOS運(yùn)算放大器TSV630/TSV630A/TSV631/TSV631A

    The TSV630 and TSV631 devices are single operational amplifiers offering low voltage, low power operation, and rail-to-rail input and ou
    發(fā)表于 09-05 10:04 ?16次下載
    軌到軌輸入/輸出60μ880千赫5V CMOS運(yùn)算放大器<b class='flag-5'>TSV</b>630/<b class='flag-5'>TSV</b>630A/<b class='flag-5'>TSV</b>631/<b class='flag-5'>TSV</b>631A

    軌到軌輸入/輸出,29μ,420 kHz的CMOS運(yùn)算放大器TSV62x,TSV62xA

    The TSV622, TSV622A, TSV623, TSV623A, TSV624, TSV
    發(fā)表于 09-05 10:58 ?4次下載
    軌到軌輸入/輸出,29μ,420 kHz的CMOS運(yùn)算放大器<b class='flag-5'>TSV</b>62x,<b class='flag-5'>TSV</b>62xA

    軌到軌輸入/輸出29μ420 kHz的CMOS運(yùn)算放大器TSV620,TSV620A,TSV621,TSV621A

    The TSV620, TSV620A, TSV621, and TSV621A are single operational amplifiers offering low volt
    發(fā)表于 09-05 11:01 ?6次下載
    軌到軌輸入/輸出29μ420 kHz的CMOS運(yùn)算放大器<b class='flag-5'>TSV</b>620,<b class='flag-5'>TSV</b>620A,<b class='flag-5'>TSV</b>621,<b class='flag-5'>TSV</b>621A

    什么是TSV封裝?TSV封裝有哪些應(yīng)用領(lǐng)域?

    硅通孔技術(shù)(Through Silicon Via, TSV)技術(shù)是一項(xiàng)高密度封裝技術(shù),正在逐漸取代目前工藝比較成熟的引線(xiàn)鍵合技術(shù),被認(rèn)為是第四代封裝技術(shù)。TSV技術(shù)通過(guò)銅、鎢、多晶硅等導(dǎo)電物質(zhì)
    發(fā)表于 08-14 15:39 ?9.1w次閱讀

    英特爾現(xiàn)代代碼:您的突破性創(chuàng)新更快

    了解英特爾的現(xiàn)代代碼計(jì)劃如何幫助開(kāi)發(fā)人員為今天和明天創(chuàng)建更快代碼
    的頭像 發(fā)表于 11-13 06:38 ?1444次閱讀

    什么是硅或TSV通路?使用TSV的應(yīng)用和優(yōu)勢(shì)

    TSV不僅賦予了芯片縱向維度的集成能力,而且它具有最短的電傳輸路徑以及優(yōu)異的抗干擾性能。隨著摩爾定律慢慢走到盡頭,半導(dǎo)體器件的微型化也越來(lái)越依賴(lài)于集成TSV的先進(jìn)封裝。
    發(fā)表于 07-25 10:09 ?811次閱讀
    什么是硅或<b class='flag-5'>TSV</b>通路?使用<b class='flag-5'>TSV</b>的應(yīng)用和優(yōu)勢(shì)

    單片機(jī)解析g代碼的方法

    的運(yùn)動(dòng)。 解析G代碼是將其轉(zhuǎn)化為單片機(jī)能夠理解和執(zhí)行的指令集。單片機(jī)解析G代碼的方法主要包括以下幾個(gè)方面:G代碼的格式
    的頭像 發(fā)表于 12-22 14:15 ?1877次閱讀