chinese直男口爆体育生外卖, 99久久er热在这里只有精品99, 又色又爽又黄18禁美女裸身无遮挡, gogogo高清免费观看日本电视,私密按摩师高清版在线,人妻视频毛茸茸,91论坛 兴趣闲谈,欧美 亚洲 精品 8区,国产精品久久久久精品免费

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

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

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

踩坑rust的partial copy導(dǎo)致metrics丟失

jf_wN0SrCdH ? 來源: RisingWave中文開源社區(qū) ? 2024-01-03 10:02 ? 次閱讀
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

作者:溫一鳴RisingWaveLabs 內(nèi)核開發(fā)工程師

在 RisingWave 的存儲代碼中,我們使用 RAII [1] 的思想來對 LSM iterator 的 metrics 進行監(jiān)控,從而避免在代碼中忘記上報 metrics 而導(dǎo)致 metrics 丟失。在實現(xiàn)中,我們設(shè)計了一個 rust 的 struct MonitoredStateStoreIterStats去收集 LSM iterator 的 metrics,去統(tǒng)計 iterator 中 key 的數(shù)量和長度,并為這個 struct 實現(xiàn)了 Drop,在這個 struct 被釋放的時候把在本地收集的 metrics 上報 prometheus。通過這種方式,我們不需要在每次 iterator 使用完后都手動上報 metrics,從而避免了由于代碼的疏忽導(dǎo)致忘記上報 metrics。

以下是一段簡化過的代碼。我們通過try_stream[2] 這個宏來封裝一個 iterator 的 stream 來收集這個 stream 的 metrics。在返回的 stream 被釋放時,stats 隨著 stream 被釋放,并調(diào)用其 drop 方法來上報收集到的 metrics。

pubstructMonitoredStateStoreIter{
inner:S,
stats:MonitoredStateStoreIterStats,
}

structMonitoredStateStoreIterStats{
total_items:usize,
total_size:usize,
storage_metrics:Arc,
}

implMonitoredStateStoreIter{
#[try_stream(ok=StateStoreIterItem,error=StorageError)]
asyncfninto_stream_inner(mutself){
letinner=self.inner;
futures::pin_mut!(inner);
whileletSome((key,value))=inner
.try_next()
.await
.inspect_err(|e|error!("Failedinnext:{:?}",e))?
{
self.stats.total_items+=1;
self.stats.total_size+=key.encoded_len()+value.len();
yield(key,value);
}
}
}

implDropforMonitoredStateStoreIterStats{
fndrop(&mutself){
self.storage_metrics
.iter_item
.observe(self.total_itemsasf64);
self.storage_metrics
.iter_size
.observe(self.total_sizeasf64);
}
}

然而,在使用過程中,我們遇到了上報的 metrics 全部為 0 的問題。

1最小復(fù)現(xiàn)

由于使用了try_stream宏來生成 stream,因此我們懷疑在try_stream生成的代碼中有 bug 導(dǎo)致 metrics 丟失。于是我們用 cargo-expand [3] 來將查看宏生成的代碼。展開后的代碼如下:

fninto_stream_inner(
mutself,
)->implStream
>{
::from_generator(staticmove|
mut__task_context:::ResumeTy,
|->::Result<(),?StorageError>{
let():()={
letinner=self.inner;
letmutinner=inner;
#[allow(unused_mut)]
letmutinner=unsafe{
::new_unchecked(
&mutinner,
)
};
whileletSome((key,value))
={
letmut__pinned=inner.try_next();
loop{
iflet::Ready(
result,
)=unsafe{
poll(Pin::as_mut(&mut__pinned),get_context(__task_context))
}{
breakresult;
}
__task_context=(yield::Pending);
}
}?
{
self.stats.total_items+=1;
self.stats.total_size+=key.encoded_len()+value.len();
__task_context=(yield::Ready((
key,
value,
)));
}
};
#[allow(unreachable_code)]
{
return::Ok(());
loop{
__task_context=(yield::Pending);
}
}
})
}

可以看到,try_stream宏生成的代碼中,包含了一個 rust generator 的閉包。閉包中收集和上報 metrics 的邏輯與原代碼基本相同,按照我們對 rust 的理解,仍然不應(yīng)該會出現(xiàn) metrics 丟失的問題。因此我們懷疑是 rust 編譯器中與 generator 相關(guān)的邏輯存在問題。在 rust playground 上,我們嘗試了以下代碼來對問題進行復(fù)現(xiàn)。

structStat{
count:usize,
vec:Vec,
}

implDropforStat{
fndrop(&mutself){
println!("count:{}",self.count);
}
}

fnmain(){

letmutstat=Stat{
count:0,
vec:Vec::new(),
};

letmutf=move||{
stat.count+=1;
1
};

println!("num:{}",f());
}

執(zhí)行以后輸出如下。

num:1
count:0

按照預(yù)期,輸出的 num 和 count 應(yīng)該都為1,因為在調(diào)用閉包 f 時stat.count += 1被調(diào)用了,但是以上代碼中遇到了和最開始同樣的問題。因此以上代碼可以作為我們問題的一個最小復(fù)現(xiàn)。

2問題分析

對以上代碼進行分析的話,我們看到閉包 f 的代碼中使用了 move,因此在閉包中使用過的對象的 ownership 應(yīng)該都會轉(zhuǎn)移到閉包中。而 struct Stats實現(xiàn)了Drop,因此Stats是不可以 partial move 的,其必須作為一個整體被 move 進入閉包。而在閉包中執(zhí)行了stats.count += 1,因此 stats 中的 count 應(yīng)該被置為1。但是從程序的輸出可以看到在 stats 被 drop 時,stats 的 count 是 0。

我們嘗試將閉包 f 改為如下代碼,來顯式的將 stats 的 ownership 給 move 進閉包里。

letmutf=move||{
letmutstat=stat;
stat.count+=1;
1
};

輸出恢復(fù)正常。

num:1
count:1

我們再次嘗試在閉包 f 中使用 stat 中的另一個字段 vec:

letmutf=move||{
let_=stat.vec.len();
stat.count+=1;
1
};

輸出同樣恢復(fù)正常。

num:1
count:1

可以看到,我們顯式地將 stat 整個 move 進閉包,或者在閉包中使用類型為 vec 的字段,都會使得 stat 的ownership 被 move 進閉包。

于是我們推測,盡管 stat 實現(xiàn)了自己的 drop 導(dǎo)致不能被 partial move,但是如果我們在 move 的閉包中只使用了 stat 中實現(xiàn)了 Copy 類型的字段,則這個字段的值會被 Copy 到閉包中,而閉包中對這個字段的修改只會作用于被 Copy 后的值,而原字段并不會改變。

3驗證猜想

我們可以通過將以上代碼編譯成二進制代碼后,對其匯編代碼進行分析,從而驗證我們的猜想。然而,編譯后的匯編代碼會過于復(fù)雜且晦澀難懂,同時編譯器對其進行的一些優(yōu)化也會改變原有的邏輯導(dǎo)致匯編代碼難以理解。因此我們打算通過分析在編譯過程中產(chǎn)生的 MIR 中間代碼來對問題進行分析。在 rust playground 上可以十分方便地生成 MIR 代碼。

首先我們對存在問題的最小復(fù)現(xiàn)代碼生成 MIR,生成后與閉包相關(guān)的 MIR 如下??梢钥吹竭@個閉包確實只包含了一個類型為 usize 的字段,這個字段的值取的是 stat 中的 count 值。

bb1:{
_1=Stat{count:const0_usize,vec:move_2};
_3={closure@src/main.rs:19:17:19:24}{stat:(_1.0:usize)};
}

而我們對后續(xù)測試中有正常輸出的代碼生成 MIR,生成后與閉包相關(guān)的 MIR 如下。可以看到這個閉包將整個 stat 的 ownership 給 move 了進去。

bb1:{
_1=Stat{count:const0_usize,vec:move_2};
_3={closure@src/main.rs:19:17:19:24}{stat:move_1};
}

于是,我們的猜想得到了驗證,在我們出現(xiàn)問題的代碼中,閉包確實沒有捕獲 stat 的 ownership。

4后續(xù)與總結(jié)

我們向 rust 社區(qū)反映了這個問題,得到的反饋是,這個是 rust 2021 后實現(xiàn)的一個 feature [4]。在 rust 2021 中,一個使用了 move 的閉包在捕獲一個 struct 的時候,會盡可能少地去捕獲 struct 中的字段。

如果一個 struct 沒有實現(xiàn) Drop,這意味著他里面的字段可以被分開 move,而閉包只會捕獲閉包中用到的字段。

如果某個被閉包使用的字段實現(xiàn)了 Copy,那他閉包并不會捕獲這個字段的 ownership,而是將這個字段 copy 一份放在閉包中。

如果一個 struct 實現(xiàn)了 Drop,那他里面的字段只能作為一個整體被捕獲。但如果閉包只使用了這個閉包中實現(xiàn)了 Copy 的字段,那這個閉包不會捕獲這個 struct,而是將使用到的字段 copy 一份。

我們的代碼中,正是因為這個行為,導(dǎo)致我們的代碼產(chǎn)生了歧義,而出現(xiàn)了 metrics 的丟失。

針對這個問題,我們認為有兩個地方有提升的空間。

首先,try_stream這個宏的封裝存在一定的問題。在使用宏來聲明代碼中,其暴露出來的使用方法是通過調(diào)用一個方法來生成 stream,而在調(diào)用方法時,如果參數(shù)是通過 move ownership 的形式傳入的,同時在生成 stream 的代碼中我們使用了這個參數(shù),那我們應(yīng)該認為這個 stream 包含了這個參數(shù)的 ownership。然而,由于這個宏在實現(xiàn)的時候使用了閉包,導(dǎo)致這個 stream 并沒有包含這個參數(shù)的 ownership,從而導(dǎo)致問題。這個是宏封裝邏輯時的問題。

其次,rust 在語言設(shè)計上,由于引入了這個閉包捕獲 ownership 的特殊邏輯,導(dǎo)致會寫出有歧義的代碼。例如,在上述代碼中,很難想象stat.count += 1并沒有去修改 stat 中的 count 值。我們也向 rust 社區(qū)反映了這個問題 [5]。

最后,回到我們最開始的問題中。要想解決 metrics 丟失的問題,在我們的代碼中,我們只需要做以下修改就能讓代碼正常運行[6]。

#[try_stream(ok=StateStoreIterItem,error=StorageError)]
asyncfninto_stream_inner(mutself){
letinner=self.inner;
...
self.stats.total_items+=1;
self.stats.total_size+=key.encoded_len()+value.len();
}

修改為

#[try_stream(ok=StateStoreIterItem,error=StorageError)]
asyncfninto_stream_inner(self){
letinner=self.inner;
letmutstats=self.stats;
...
stats.total_items+=1;
stats.total_size+=key.encoded_len()+value.len();
}

審核編輯:湯梓紅

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

    關(guān)注

    4

    文章

    1466

    瀏覽量

    42803
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4965

    瀏覽量

    73862
  • Rust
    +關(guān)注

    關(guān)注

    1

    文章

    240

    瀏覽量

    7570

原文標題:踩坑 rust 的 partial copy 導(dǎo)致 metrics 丟失

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

收藏 人收藏
加入交流群
微信小助手二維碼

掃碼添加小助手

加入工程師交流群

    評論

    相關(guān)推薦
    熱點推薦

    有沒有關(guān)于STM32入門經(jīng)驗分享

    有沒有關(guān)于STM32入門經(jīng)驗分享
    發(fā)表于 10-13 06:52

    NodeMCU開發(fā)板經(jīng)歷分享

    寫在前面今天入手了一個NodeMCU的板子,準備學(xué)習(xí)一下物聯(lián)網(wǎng)相關(guān)的知識。不過由于博主學(xué)藝不精,在第一步燒寫固件上就了,所以就想著把自己的經(jīng)歷寫出來分享給大家,希望能有一些幫助
    發(fā)表于 11-01 07:55

    Linux學(xué)習(xí)過程過的與如何解決

    Linux記錄記錄Linux學(xué)習(xí)過程過的與如何解決1解決方法:F10進入BIOS使能
    發(fā)表于 11-04 08:44

    移植debian系統(tǒng)過的

    基本的linux系統(tǒng),板子的交叉編譯器是arm-linux-gnueabihf-gcc,這給我?guī)砹瞬簧俚穆闊?,以至于想重新移植一下debian系統(tǒng)。ok,轉(zhuǎn)入正題,說說這兩天我吧。首先...
    發(fā)表于 12-14 08:42

    STM32編程常有哪些?

    STM32編程常有哪些?
    發(fā)表于 12-17 06:15

    記錄寫SAM4S的bootloader所

    記錄寫SAM4S的bootloader所
    發(fā)表于 01-24 07:16

    嵌入式Linux記錄

    Linux記錄記錄Linux學(xué)習(xí)過程過的與如何解決1解決方法:F10進入BIOS使能
    發(fā)表于 11-01 17:21 ?10次下載
    嵌入式Linux<b class='flag-5'>踩</b><b class='flag-5'>坑</b>記錄

    Arduino-IDE配置ESP32-CAM開發(fā)環(huán)境過的那些

    Arduino-IDE配置ESP32-CAM開發(fā)環(huán)境過的那些
    發(fā)表于 11-30 18:36 ?24次下載
    Arduino-IDE配置ESP32-CAM開發(fā)環(huán)境<b class='flag-5'>踩</b>過的那些<b class='flag-5'>坑</b>

    STM32CubeIDE+FREERTOS記錄

    STM32CubeIDE+FREERTOS記錄
    發(fā)表于 12-05 18:06 ?15次下載
    STM32CubeIDE+FREERTOS<b class='flag-5'>踩</b><b class='flag-5'>坑</b>記錄

    搭建D1s RT-Smart開發(fā)環(huán)境筆記

    作為一個linux新手想要嘗試RT-Smart的開發(fā),但是網(wǎng)上教程前輩們的linux環(huán)境都是已經(jīng)相對完備的,因此像我這樣新手在搭建環(huán)境時常常缺這缺那的導(dǎo)致報錯,經(jīng)過一段時間的終于搞定了,因此和大家分享我遇到的
    的頭像 發(fā)表于 09-28 16:26 ?1488次閱讀
    搭建D1s RT-Smart開發(fā)環(huán)境<b class='flag-5'>踩</b><b class='flag-5'>坑</b>筆記

    推挽電路的,你過沒?

    推挽電路的,你過沒?
    的頭像 發(fā)表于 11-24 16:25 ?1905次閱讀
    推挽電路的<b class='flag-5'>坑</b>,你<b class='flag-5'>踩</b>過沒?

    關(guān)于圖像傳感器圖像質(zhì)量的四大誤區(qū)!你過幾個?

    關(guān)于圖像傳感器圖像質(zhì)量的四大誤區(qū)!你過幾個?
    的頭像 發(fā)表于 11-27 16:56 ?1028次閱讀
    關(guān)于圖像傳感器圖像質(zhì)量的四大誤區(qū)!你<b class='flag-5'>踩</b>過幾個<b class='flag-5'>坑</b>?

    反相輸入放大器的,你過沒有?

    反相輸入放大器的,你過沒有?
    的頭像 發(fā)表于 12-06 15:35 ?1387次閱讀
    反相輸入放大器的<b class='flag-5'>坑</b>,你<b class='flag-5'>踩</b>過沒有?

    UWB自動跟隨技術(shù)原理、算法融合優(yōu)化和實錄

    UWB為什么是最靠譜的自動跟隨技術(shù)?原理是什么?需要做什么算法融合、優(yōu)化?我們在開發(fā)過程中過的。
    的頭像 發(fā)表于 08-14 17:45 ?1487次閱讀
    UWB自動跟隨技術(shù)原理、算法融合優(yōu)化和<b class='flag-5'>踩</b><b class='flag-5'>坑</b>實錄

    K8s生產(chǎn)環(huán)境10大記錄復(fù)盤

    這篇文章記錄了我這些年在 K8s 生產(chǎn)環(huán)境過的。每一個案例都是血淚教訓(xùn),有些甚至導(dǎo)致了生產(chǎn)事故。希望通過分享這些經(jīng)歷,能幫助大家避免重蹈覆轍。
    的頭像 發(fā)表于 02-05 15:51 ?218次閱讀