ஒவ்வொரு வார்த்தையையும் ஒருமுறை எண்ணுவதற்கு நான் இருமுறை ஹாஷ் செய்தேன்

கடந்த வாரம் நான் ஒரு லாக் பார்சரை (log parser) ஆய்வு செய்தேன். அது ஒரு தினசரி லாக்-இல் உள்ள பிழை குறியீடுகளை (error codes) எண்ணுகிறது. இதன் முக்கிய தர்க்கம் (logic) ஒரு எளிய அகராதி (dictionary) புதுப்பிப்பு ஆகும்.

பெரும்பாலான டெவலப்பர்கள் இதை இவ்வாறு எழுதுவார்கள்:

if (counts.TryGetValue(code, out int c))
    counts[code] = c + 1;
else
    counts[code] = 1;

இந்தக் குறியீடு கீயை (key) இருமுறை ஹாஷ் செய்கிறது. TryGetValue என்பது ஹாஷைக் கணக்கிட்டு மற்றும் பக்கெட்டை (bucket) ஆய்வு செய்வதன் மூலம் அந்தப் பதிவைக் கண்டறிகிறது. பின்னர், மதிப்பைத் புதுப்பிக்க இண்டெக்சர் (indexer) அதே வேலையை மீண்டும் செய்கிறது. ஒவ்வொரு டோக்கனுக்கும் ஒரே கீ மற்றும் ஒரே ஹாஷிற்கு நீங்கள் இரண்டு முறை CPU சுழற்சிகளை (cycles) வீணடிக்கிறீர்கள்.

நீங்கள் இரண்டாவது முறையைத் தவிர்க்கலாம். CollectionsMarshal.GetValueRefOrAddDefault-ஐப் பயன்படுத்தவும்.

ref int slot = ref CollectionsMarshal.GetValueRefOrAddDefault(counts, code, out _);
slot++;

இந்த முறை ஒரே படிநிலையில் ஸ்லாட்டை (slot) கண்டறியும் அல்லது உருவாக்கும். இது சேமிப்பகத்திற்கு (storage) நேரடியாக ஒரு குறிப்பினை (reference) உங்களுக்கு வழங்குகிறது. நீங்கள் ஒரு முறை ஹாஷ் மற்றும் ஒரு முறை பக்கெட் ஆய்வை மட்டுமே செய்கிறீர்கள். பின்னர் அந்த மதிப்பை அங்கேயே மாற்றியமைக்கிறீர்கள்.

நான் இதை 5 மில்லியன் டோக்கன்களுடன் சோதித்தேன்.

முடிவுகள்: • TryGetValue + indexer: 160.0 ms • GetValueRefOrAddDefault: 95.0 ms

ஒரே ஒரு தேடல் (one-lookup) கொண்ட பதிப்பு 1.7 மடங்கு வேகமானது.

முக்கியமாக, மெமரி ஒதுக்கீடு (memory allocations) மாறாமல் அப்படியே இருந்தது. இந்தத் தந்திரம் மெமரியைச் சேமிக்காது. இது CPU-வை மட்டுமே சேமிக்கிறது. உங்கள் குறியீடு Garbage Collection காரணமாக மெதுவாக இருந்தால், இந்த மாற்றம் எதையும் செய்யாது. உங்கள் குறியீடு அதிக எண்ணிக்கையைக் கணக்கிடுவதால் (heavy counting) மெதுவாக இருந்தால், இது உதவும்.

உங்கள் லூப் (loop) ஏற்கனவே உள்ள கீகளுக்குப் பலமுறை புதுப்பிப்புகளைச் செய்யும்போது இதைப் பயன்படுத்தவும். புதுப்பிப்புகளுக்கும் (updates) புதிய சேர்க்கைகளுக்கும் (inserts) இடையிலான விகிதம் அதிகரிக்கும் போது இதன் பயன் அதிகமாகும்.

ஒரு எச்சரிக்கை: இந்த குறிப்பு (reference) அகராதியின் உள் சேமிப்பகத்தைக் (internal dictionary storage) குறிக்கிறது. அடுத்த கட்டமைப்பு மாற்றம் (structural change) ஏற்படும் வரை மட்டுமே இது செல்லுபடியாகும். நீங்கள் கீகளைச் சேர்க்கும்போதோ அல்லது நீக்கும்போதோ அந்தப் குறிப்பைப் பிடித்து வைத்திருக்க வேண்டாம். குறிப்பைப் பெற்று, மதிப்பை மாற்றிவிட்டு, அடுத்த கட்டத்திற்குச் செல்லவும்.

பெரும்பாலான பணிகளுக்கு TryGetValue-ஐப் பயன்படுத்தவும். அது வாசிப்பதற்கு எளிதானது. உங்கள் அகராதி லூப் ஒரு செயல்திறன் தடையாகும் (performance bottleneck) போது மட்டுமே ref பதிப்பைப் பயன்படுத்தவும்.

Source: https://dev.to/ssukhpinder/i-hashed-every-word-twice-to-count-it-once-1mg9