मी फक्त फेकून देण्यासाठी ७.८ MB च्या कीज अलोकेट केल्या

एका प्रोफायलरने (profiler) माझ्या कोडमधील एक छुपा खर्च उघड केला.

माझा रिक्वेस्ट हँडलर (request handler) एका मोठ्या स्ट्रिंगमधून टोकन्स वाचतो आणि त्यांच्या वजनाची (weights) बेरीज करतो. लॉजिक बरोबर काम करत होते, पण मेमरीमध्ये अडचण येत होती.

एका 'हॉट लूप'मध्ये (hot loop) मी ७.८ MB च्या स्ट्रिंग्स अलोकेट केल्या—प्रत्येक डिक्शनरी लूकअपसाठी एक नवीन की—आणि त्यानंतर ती की लगेच फेकून दिली.

याचे मुख्य कारण Substring होते. प्रत्येक स्लाईस (slice) एक नवीन स्ट्रिंग ऑब्जेक्ट तयार करत होता.

२००,००० टोकन्स

  • Method A (Substring): २३.४ ms, ७,८१२ KB अलोकेट झाले.
  • Method B (Span Lookup): १४.९ ms, ० KB अलोकेट झाले.

Method B १.५ पट वेगाने चालते आणि कोणताही कचरा (garbage) निर्माण करत नाही.

.NET 9 मध्ये तुम्ही GetAlternateLookup कॉल करू शकता. नवीन स्ट्रिंगऐवजी, तुम्ही ReadOnlySpan<char> पास करू शकता, जे मूळ स्ट्रिंगवर फक्त एक 'विंडो' (window) आहे—त्यात कोणतीही कॉपीिंग प्रक्रिया होत नाही.

डिक्शनरी थेट अस्तित्वात असलेल्या कीजच्या विरुद्ध स्पॅन (span) हॅश करते, ज्यामुळे अलोकेशनशिवाय तेच निकाल मिळते.

लक्षात ठेवण्यासारख्या गोष्टी

  • StringComparer.Ordinal आणि StringComparer.OrdinalIgnoreCase सोबत काम करते.
  • जर तुम्ही अल्टरनेट लूकअप सपोर्ट नसलेला कस्टम कंपॅरर (custom comparer) वापरला, तर रनटाइमला एरर (throw) येईल.
  • पार्सर्स (parsers), लॉग प्रोसेसर्स (log processors) किंवा CSV स्कॅनर्स (scanners) सारख्या 'हॉट पाथ्स'साठी (hot paths) आदर्श आहे.
  • फक्त काही मोजक्या कीज असलेल्या साध्या लूकअपसाठी याचा वापर टाळा.

आता मी माझ्या कोडमध्ये 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