Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
RCU資料
Search
Yasuhiro Ohara
December 28, 2024
Programming
220
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
RCU資料
Yasuhiro Ohara
December 28, 2024
More Decks by Yasuhiro Ohara
See All by Yasuhiro Ohara
SRv6-mobile VPP plugin Performance Optimization
yasuhironet
2
720
sdplane wiretap 製品紹介
yasuhironet
0
42
Other Decks in Programming
See All in Programming
全PRの83%がAIレビューだけでマージできるようになった開発組織はその後どうなったか
athug
0
1k
複数の Claude Code が"放置"されてしまう問題をCLI ダッシュボードを自作して解決した話
sumihiro3
1
610
【やさしく解説 設計編 #0】DDDのコード、読めるのに分からない人へ
panda728
PRO
2
290
20260722_microCMSで考える、AI時代のコンテンツ運用設計
yosh1
0
190
ビデオ通話が繋がる0.2秒で何が起きているのか
supurazako
2
160
『コードを書く以外の』エンジニアリング〜課金基盤移行プロジェクト推進のためのTips4選
yuriko1211
0
560
テーブルをDELETEした
yuzneri
0
130
AIが無かった頃の素敵な出会いの話
codmoninc
1
340
琵琶湖の水は止められてもNet--HTTPのリトライは止められない / You might be able to stop the water flow of Lake Biwa but you can't stop Net::HTTP retries
luccafort
PRO
0
500
jsmini JavaScript Engine を作ってみた話
yosuke_furukawa
PRO
0
280
壊れたパーサから始める関数型設計と構成的なパーサ #fp_matsuri
raiga0310
2
420
React本体のコードリーディング
high_g_engineer
1
120
Featured
See All Featured
Design in an AI World
tapps
1
270
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
470
RailsConf 2023
tenderlove
30
1.5k
Digital Projects Gone Horribly Wrong (And the UX Pros Who Still Save the Day) - Dean Schuster
uxyall
1
2.2k
Rails Girls Zürich Keynote
gr2m
96
14k
How to make the Groovebox
asonas
2
2.3k
The Curious Case for Waylosing
cassininazir
1
440
Designing Dashboards & Data Visualisations in Web Apps
destraynor
232
55k
Neural Spatial Audio Processing for Sound Field Analysis and Control
skoyamalab
0
390
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
650
職位にかかわらず全員がリーダーシップを発揮するチーム作り / Building a team where everyone can demonstrate leadership regardless of position
madoxten
64
56k
Mozcon NYC 2025: Stop Losing SEO Traffic
samtorres
1
450
Transcript
RCU: Read-Copy-Update • マルチスレッドにおけるリーダーライターロックの代わり。 ロックレス • 参照するデータを変更の世代分⽤意して、すげ替える • 参照スレッド側で(スレッドローカル︖)コピーを持ってそれを読む から、Read-Copy
という名か︖すげ替える部分が Update︖ • QSBR がすごい • RCUには他にも memory barrier とか種類がある • QSBR: Quiescent State Based Reclamation • 多数の参照スレッド、少数(たぶん単⼀を想定)の更新スレッド • 参照スレッド側のオーバヘッドがほぼ皆無︕ • 本資料は QSBR に特化
RCU利⽤例︓⾼速DPDKルータ • RCU︓ロックを回避し、⾼速な同期処理する重要な技術 • DPDK pthreadがコアごとに独⽴して⾼速パケット処理、その間を RCU で調停 • RCUで共有するデータ︓経路表、ポート設定、ACLフィルター設定
DPDK P1 P4 P2 P3 CPU core1 CPU core2 CPU core3 CPU core4 OS l2fwd Forwarder P1/P2 l3fwd Forwarder P3/P4 lthread_main RCU • lthread_main: • startup_config • console_shell • tap_handler • stat_collector • l2fwd • (l3fwd-lpm)
操作関数 • rcu_assign_pointer()︓ • ポインタ登録。更新スレッドが使⽤。 • rcu_dereference()︓ • ポインタ取得。参照スレッドが毎回の read
critical section の先頭で使⽤。 • urcu_qsbr_quiescent_state()︓ • read critical section 外で、read critical section 外だということを、報告 する関数。参照スレッドが使⽤。 • urcu_qsbr_synchronize_rcu()︓ • 前スレッドが read critical section を⼀回以上抜けるまで待つ。更新スレッ ドが使⽤。この関数を呼び出し、帰って来れば、古い共有データのポインタ を保持している参照スレッドは居ないことが保証される。 • urcu_qsbr_register_thread()︓参照スレッド登録。 • urcu_qsbr_unregister_thread()︓参照スレッド登録の解除。
rcu-qsbr 更新スレッド 時間 データ 参照スレッド 参照スレッドのループの ⼀繰り返し期間は、 スレッド間でも、 スレッド内でも、 ⼀定ではない
参照スレッド 参照スレッド ループ quiescent_state() rcu_assign_pointer() read critical section synchronize_rcu() reclamation: 回収、再利⽤(削除) ⾚か⻘か不明な read critical section ⾚を保持する参照スレッドは居ない (だから消せる) read critical sectionは 毎回 rcu_dereference() し直して最新のポインタ取得 データ更新
Userscace RCU 実装内部 • カウンタを使ってスレッド間の緩い同期を図る • カウンタなのでほぼオーバヘッド無し • rcu_gp_ctr 変数(unsigned
long)。rcu_global_pointer_counter の略か。 • TLS(Thread-Local Storage)を使⽤。 • futex というカーネルサービスを使ってスレッドの WAKE/WAIT を実現 している • つまり、完全にユーザスペースで動くわけではなく、カーネル futex サービスを利 ⽤してユーザスペースでも機能する RCU ライブラリ、という位置付け。 • quiescent_state()︓ • rcu_gp_ctr を⾃スレッドローカルで更新し、そのカウンタ(カウント数)で待って るスレッドを futex で wakeup。 • rcu_synchronize()︓ • futex で全スレッドを待つ。
urcu-qsbr 使い⽅(更新スレッド) void *rcu_global_ptr_rib; void rib_replace () { #if HAVE_LIBURCU_QSBR
struct rib *new, *old; /* allocate new */ new = malloc (sizeof (struct rib)); /* retrieve old */ old = rcu_dereference (rcu_global_ptr_rib); if (old) memcpy (new, old, sizeof (struct rib)); /* update new */ rib_edit (new) ... /* assign new */ rcu_assign_pointer (rcu_global_ptr_rib, new); /* wait all readers to update. */ urcu_qsbr_synchronize_rcu (); /* reclaim old */ free (old); #endif /*HAVE_LIBURCU_QSBR*/ }
urcu-qsbr 使い⽅(参照スレッド)(1) #if HAVE_LIBURCU_QSBR urcu_qsbr_register_thread (); #endif /*HAVE_LIBURCU_QSBR*/ /* main
loop */ while (! force_quit && ! force_stop[lcore_id]) { #if HAVE_LIBURCU_QSBR urcu_qsbr_read_lock (); rib = (struct rib *) rcu_dereference (rcu_global_ptr_rib); #endif /*HAVE_LIBURCU_QSBR*/ パケット受信(); パケット処理(); #if HAVE_LIBURCU_QSBR urcu_qsbr_read_unlock (); urcu_qsbr_quiescent_state (); #endif /*HAVE_LIBURCU_QSBR*/ loop_counter++; } #if HAVE_LIBURCU_QSBR urcu_qsbr_unregister_thread (); #endif /*HAVE_LIBURCU_QSBR*/ } read critical section
urcu-qsbr 使い⽅(参照スレッド)(2) #if HAVE_LIBURCU_QSBR urcu_qsbr_register_thread (); #endif /*HAVE_LIBURCU_QSBR*/ /* main
loop */ while (! force_quit && ! force_stop[lcore_id]) { #if HAVE_LIBURCU_QSBR urcu_qsbr_read_lock (); rib = (struct rib *) rcu_dereference (rcu_global_ptr_rib); #endif /*HAVE_LIBURCU_QSBR*/ パケット受信(); パケット処理(); #if HAVE_LIBURCU_QSBR urcu_qsbr_read_unlock (); urcu_qsbr_quiescent_state (); #endif /*HAVE_LIBURCU_QSBR*/ loop_counter++; } #if HAVE_LIBURCU_QSBR urcu_qsbr_unregister_thread (); #endif /*HAVE_LIBURCU_QSBR*/ } read critical section read_lock(), read_unlock()は qsbr の場合は本当に何もしない、 呼ばなくても良い
urcu-qsbr 使い⽅(参照スレッド)(3) #if HAVE_LIBURCU_QSBR urcu_qsbr_register_thread (); #endif /*HAVE_LIBURCU_QSBR*/ /* main
loop */ while (! force_quit && ! force_stop[lcore_id]) { #if HAVE_LIBURCU_QSBR rib = (struct rib *) rcu_dereference (rcu_global_ptr_rib); #endif /*HAVE_LIBURCU_QSBR*/ パケット受信(); パケット処理(); #if HAVE_LIBURCU_QSBR urcu_qsbr_quiescent_state (); #endif /*HAVE_LIBURCU_QSBR*/ loop_counter++; } #if HAVE_LIBURCU_QSBR urcu_qsbr_unregister_thread (); #endif /*HAVE_LIBURCU_QSBR*/ } read critical section read_lock(), read_unlock()は qsbr の場合は本当に何もしない、 呼ばなくても良い
urcu-qsbr 使い⽅(まとめ) 更新スレッド︓ void *var; writer(){ void *old, *new; new
= create_new (); rcu_assign_pointer (var, new); synchronize_rcu (); free (old); } 参照スレッド︓ reader() { urcu_qsbr_register_thread (); while(1) { rcu_quiescent_state (); //rcu_read_lock(); p = rcu_dereference (var); ... //critical section using p. //rcu_read_unlock(); } urcu_qsbr_unregister_thread (); }
注意点︓ • 利⽤ユーザ側が⾃分で守らなければいけないルール︓ • read critical section の外から、中に⼊った時には、 rcu_dereference() し
て最新のポインタを取得することがルール。 • quiescent_state() の前後で、同じ古いポインタを維持・参照しないこと。 rcu_dereference() し直すこと。 • 参照スレッドは全て登録(register_thread())すること。 • ⾃由度︓ • ⼀ループ繰り返しの中では、read critical section の外であれば、 quiescent_state() は前でも後ろでも良い。呼び出し(報告)場所はどこで も⼤丈夫。(毎回rcu_dereference()していれば、)read critical section が細切れにたくさんあって、その途中のどこかでも⼤丈夫。複数回呼んでも ⼤丈夫。 • 更新スレッド⾃⾝を参照スレッド登録するのはちょっと複雑で危険。 デッドロックっぽいスレッド停⽌が起きた。
ポート設定コンフリクト • ポート設定をrcu変更すると き、スレッド間でタイミング 合わない(同期しない)と、 同じポート(queue)にアク セスして、クラッシュする t1 t2 p0
p1 thread: t1 port: 0 thread: t2 port: 1 thread: t1 port: 0 thread: t1 port: 1 rcu x
ポート設定コンフリクトの解消 • rcuで、コンフリクトが解消 できるような中間状態の設定 を追加することで、スレッド 間の同期も実現できる • (差分計算が⾯倒くさければ、 緑の中間状態を空にしてしま えば、効率性・性能を犠牲に
して安全性を獲得できる) t1 t2 p0 p1 thread: t1 port: 0 thread: t2 port: 1 thread: t1 port: 0 rcu t1 t2 p0 p1 thread: t1 port: 0 thread: t1 port: 0 thread: t1 port: 1 rcu