Spice: Zig में sub-nanosecond overhead के साथ सूक्ष्म parallel processing तकनीक
(github.com/judofyr)Spice: Sub-nanosecond Overhead वाली parallel processing
Spice Zig में heartbeat scheduling का उपयोग करके बेहद कुशल parallel processing हासिल करता है
- sub-nanosecond overhead: किसी फ़ंक्शन में parallel processing जोड़ने पर भी केवल एक nanosecond से कम overhead आता है
- कोई contention नहीं: threads एक ही काम के लिए प्रतिस्पर्धा नहीं करते। सिस्टम में threads जोड़ने पर भी प्रोग्राम धीमा नहीं होता
प्रदर्शन तुलना
- Rayon (Rust): 4 threads पर लगभग 15ns का overhead। 16 threads पर लगभग 14 गुना speedup
- Spice (Zig): 16 threads पर लगभग 11 गुना speedup। overhead इतना कम कि base performance के लगभग समान
छोटे tree में प्रदर्शन
- छोटा tree: प्रोग्राम का कुल execution time 1.56 microsecond। threads बढ़ने पर प्रदर्शन घटता है
- parallel processing की सामान्य समझ: पर्याप्त काम न हो तो parallel processing का फ़ायदा नहीं होता
Spice का लक्ष्य
- लक्ष्य: parallel processing जोड़ने पर भी प्रोग्राम धीमा न हो
- कम execution time: execution time छोटा हो तो multithreading काम नहीं करती। जोड़े गए अतिरिक्त threads wait state में चले जाते हैं
Spice का उपयोग
const spice = @import("spice");
fn sum(t: *spice.Task, node: *const Node) i64 {
var res: i64 = node.val;
if (node.left) |left_child| {
if (node.right) |right_child| {
var fut = spice.Future(*const Node, i64).init();
fut.fork(t, sum, right_child);
res += t.call(i64, sum, left_child);
if (fut.join(t)) |val| {
res += val;
} else {
res += t.call(i64, sum, right_child);
}
return res;
}
res += t.call(i64, sum, left_child);
}
if (node.right) |right_child| {
res += t.call(i64, sum, right_child);
}
return res;
}
- हर parallel फ़ंक्शन को पैरामीटर के रूप में task लेना चाहिए
- फ़ंक्शन को सीधे call न करें,
t.callका उपयोग करें forkको call करके दूसरे thread पर काम सेट करें- फ़ंक्शन को अपने स्तर पर अर्थपूर्ण काम करना चाहिए
joinको call करके दूसरे thread के काम पूरा होने का इंतज़ार करें- अगर
joinnullलौटाए, तो काम खुद करना चाहिए
Work-stealing और अक्षमियाँ
- Work-stealing: हर thread की अपनी local work queue होती है। queue खाली होने पर वह दूसरे thread का काम चुरा लेता है
- अक्षमियाँ: dynamic dispatch, non-local work queue, spin lock
implementation details
static dispatch optimization
- code block का parallel execution:
forkऔरjoinका उपयोग करके code blocks को parallel में चलाया जाता है - duplication elimination: अगर code block दूसरे thread पर execute नहीं होता, तो उसे sequentially चलाया जाता है
कम-overhead heartbeat signal
- heartbeat scheduling: हर 100 microsecond में local work queue की जाँच करके काम दूसरे threads को भेजा जाता है
- efficiency: heartbeat न होने पर भी फ़ंक्शन को कुशलता से काम करना चाहिए
global mutex
- global mutex का उपयोग: contention न होने पर global mutex कोई समस्या नहीं है
branchless doubly linked list
- doubly linked list: work queue को manage करने के लिए इस्तेमाल होती है। यह बिना branching के काम करती है
stack usage को न्यूनतम करना
- stack usage optimization:
Futureकी state को न्यूनतम रखकर stack usage कम किया जाता है
register के ज़रिए value passing
- register का उपयोग: performance optimization के लिए
Taskstruct के fields को registers के ज़रिए pass किया जाता है
benchmark
- benchmark: शुरुआती development एक ही benchmark के इर्द-गिर्द हुआ
आभार
- heartbeat scheduling research: इसका development कई research papers के आधार पर हुआ
सीमाएँ
- constraints: गलत तरीके से उपयोग करने पर अजीब behavior हो सकता है
- testing की कमी: test coverage पर्याप्त नहीं है
- array/slice support की कमी: arrays/slices के लिए parallel processing support कम है
- documentation की कमी: उपयोग के बारे में documentation कम है
- अतिरिक्त benchmarks की कमी: और benchmarks की ज़रूरत है
- error handling: error handling पर पर्याप्त विचार नहीं किया गया है
- ReleaseSafe testing की कमी: ReleaseSafe mode में testing की ज़रूरत है
FAQ
- नाम की उत्पत्ति: इसका मतलब बहुत सूक्ष्म parallel processing है
- Zig में implement करने की वजह: इसे कई भाषाओं में implement किया जा सकता है
- Rust में safe parallel processing: Rust की सख्त semantics के कारण शुरुआती ideas को explore करना कठिन है
GN⁺ का सार
- Spice Zig में बेहद कुशल parallel processing देने वाला एक research project है
- sub-nanosecond overhead और contention-free parallel processing के साथ performance को अधिकतम करता है
- heartbeat scheduling के ज़रिए काम को कुशलता से वितरित करता है
- constraints और testing की कमी जैसी सीमाएँ मौजूद हैं
- Rust जैसी दूसरी भाषाओं में भी इसी तरह के approach को explore करना उपयोगी हो सकता है
1 टिप्पणियां
Hacker News राय
यह implementation हालिया research "heartbeat scheduling" पर आधारित है, और parallelism बनाने के overhead को वितरित करके dynamic automatic granularity control हासिल करता है
मैंने code की details नहीं पढ़ीं, लेकिन "sub-nanosecond overhead" भ्रामक लगता है और marketing term जैसा है
मैं इस क्षेत्र से परिचित नहीं हूँ, लेकिन प्रस्तुत concurrency model पसंद आया
यह दिलचस्प research work है, और code के अलावा इसमें अच्छा तर्क और अच्छी तरह लिखा गया documentation भी है
project की limitations की सूची:
विवरण के अनुसार, यह implementation nanosecond-स्तर की latency पाने के लिए workers द्वारा busy-waiting का उपयोग करता है
मैं सोचता हूँ कि क्या task producer, consumer को busy-waiting के बिना जगाने का कोई तेज़ तरीका है
FUTEX_WAKEoperation के ज़रिए consumer को जगाने पर होने वाली सामान्य penalty को आधा किया जा सकता हैयह दिलचस्प और बेहतरीन papers से जुड़ा हुआ है
cooperative scheduling कई patterns की बुनियाद है जिनके metrics शानदार होते हैं
शानदार
benchmark से संबंधित README भी देखें: