7.8 MB کی وہ کیز جو میں نے صرف ضائع کرنے کے لیے مختص کیں
ایک پروفائلر (profiler) نے میرے کوڈ میں ایک چھپے ہوئے اخراجات (cost) کو بے نقاب کیا۔
میرا ریکوسٹ ہینڈلر (request handler) ایک بہت بڑی اسٹرنگ سے ٹوکنز (tokens) پڑھتا ہے اور ان کے وزن (weights) کا مجموعہ نکالتا ہے۔ منطق (logic) تو درست کام کر رہی تھی، لیکن میموری نہیں۔
ایک ہاٹ لوپ (hot loop) کے اندر میں نے 7.8 MB کی اسٹرنگز مختص کیں—ڈکشنری لک اپ (dictionary lookup) کے ہر عمل کے لیے ایک نئی کی (key)—اور پھر فوراً اس کی کو پھینک دیا۔
اس کی وجہ Substring تھی۔ ہر سلائس (slice) ایک نئی اسٹرنگ آبجیکٹ بنا رہا تھا۔
200,000 ٹوکنز
- Method A (Substring): 23.4 ms، 7,812 KB مختص ہوئے۔
- Method B (Span Lookup): 14.9 ms، 0 KB مختص ہوئے۔
Method B 1.5 گنا زیادہ تیز چلتا ہے اور کوئی گبج (garbage) پیدا نہیں کرتا۔
.NET 9 میں آپ GetAlternateLookup کو کال کر سکتے ہیں۔ ایک نئی اسٹرنگ کے بجائے، آپ ReadOnlySpan<char> پاس کرتے ہیں، جو اصل اسٹرنگ پر محض ایک ونڈو (window) کی طرح ہے—اس میں کوئی کاپی نہیں ہوتی۔
ڈکشنری اس سپین (span) کو براہ راست موجودہ کیز (keys) کے خلاف ہیش (hash) کرتی ہے، جس سے بغیر کسی میموری مختص کیے وہی نتیجہ حاصل ہوتا ہے۔
یاد رکھنے والی باتیں
StringComparer.OrdinalاورStringComparer.OrdinalIgnoreCaseکے ساتھ کام کرتا ہے۔- اگر آپ کوئی ایسا کسٹم کمپریرر (custom comparer) استعمال کریں جس میں آلٹرنیٹ لک اپ سپورٹ نہ ہو، تو یہ رن ٹائم (runtime) پر ایرر دے گا۔
- ہاٹ پاتھز (hot paths) جیسے کہ پارسرز (parsers)، لاگ پروسیسرز (log processors)، یا CSV اسکینرز کے لیے بہترین ہے۔
- صرف چند کیز والے سادہ لک اپس کے لیے اسے استعمال نہ کریں۔
اب میں اپنے کوڈ میں Substring کے بعد TryGetValue کے استعمال کو تلاش کرتا ہوں۔ یہ پیٹرن میموری کی بہت بڑی مقدار ضائع کرتا ہے۔
Source: https://dev.to/ssukhpinder/78-mb-of-keys-i-allocated-just-to-throw-away-50mn
Optional learning community: https://github.com/ssukhpinder/dev-to-code-samples/tree/main/023-dictionary-alternate-lookup
