ಒಂದೇ ಒಂದು ಬಾರಿ ಎಣಿಸಲು ನಾನು ಪ್ರತಿಯೊಂದು ಪದವನ್ನು ಎರಡು ಬಾರಿ ಹ್ಯಾಶ್ ಮಾಡಿದೆ

ಕಳೆದ ವಾರ ನಾನು ಒಂದು ಲಾಗ್ ಪಾರ್ಸರ್ ಅನ್ನು ಪ್ರೊಫೈಲ್ ಮಾಡಿದೆ. ಇದು ದೈನಂದಿನ ಲಾಗ್‌ನಲ್ಲಿನ ಎರರ್ ಕೋಡ್‌ಗಳನ್ನು ಎಣಿಸುತ್ತದೆ. ಇದರ ಮೂಲ ತರ್ಕವು (logic) ಒಂದು ಸರಳ ಡಿಕ್ಷನರಿ ಅಪ್‌ಡೇಟ್ ಆಗಿದೆ.

ಹೆಚ್ಚಿನ ಡೆವಲಪರ್‌ಗಳು ಇದನ್ನು ಈ ರೀತಿ ಬರೆಯುತ್ತಾರೆ:

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

ಈ ಕೋಡ್ ಕೀಯನ್ನು (key) ಎರಡು ಬಾರಿ ಹ್ಯಾಶ್ ಮಾಡುತ್ತದೆ. TryGetValue ಹ್ಯಾಶ್ ಅನ್ನು ಲೆಕ್ಕಹಾಕುವ ಮೂಲಕ ಮತ್ತು ಬಕೆಟ್ ಅನ್ನು ಹುಡುಕುವ ಮೂಲಕ ಎಂಟ್ರಿಯನ್ನು ಪತ್ತೆಹಚ್ಚುತ್ತದೆ. ನಂತರ, ಮೌಲ್ಯವನ್ನು ಅಪ್‌ಡೇಟ್ ಮಾಡಲು ಇಂಡೆಕ್ಸರ್ (indexer) ಅದೇ ಕೆಲಸವನ್ನು ಮತ್ತೊಮ್ಮೆ ಮಾಡುತ್ತದೆ. ನೀವು ಪ್ರತಿ ಟೋಕನ್‌ಗೆ ಒಂದೇ ಕೀ ಮತ್ತು ಒಂದೇ ಹ್ಯಾಶ್ ಮೇಲೆ ಎರಡು ಬಾರಿ CPU ಸೈಕಲ್‌ಗಳನ್ನು ವ್ಯರ್ಥ ಮಾಡುತ್ತೀರಿ.

ನೀವು ಎರಡನೇ ಹಂತವನ್ನು ಬಿಡಬಹುದು. CollectionsMarshal.GetValueRefOrAddDefault ಬಳಸಿ.

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

ಈ ವಿಧಾನವು ಒಂದೇ ಹಂತದಲ್ಲಿ ಸ್ಲಾಟ್ ಅನ್ನು ಪತ್ತೆಹಚ್ಚುತ್ತದೆ ಅಥವಾ ರಚಿಸುತ್ತದೆ. ಇದು ನಿಮಗೆ ಸ್ಟೋರೇಜ್‌ನ ನೇರ ರೆಫರೆನ್ಸ್ ಅನ್ನು ನೀಡುತ್ತದೆ. ನೀವು ಒಂದು ಹ್ಯಾಶ್ ಮತ್ತು ಒಂದು ಬಕೆಟ್ ವಾಕ್ ಅನ್ನು ಮಾಡ terlೊಕ, ನಂತರ ಮೌಲ್ಯವನ್ನು ಸ್ಥಳದಲ್ಲೇ ಬದಲಾಯಿಸಬಹುದು (mutate).

ನಾನು ಇದನ್ನು 5 ಮಿಲಿಯನ್ ಟೋಕನ್‌ಗಳೊಂದಿಗೆ ಪರೀಕ್ಷಿಸಿದೆ.

ಫಲಿತಾಂಶಗಳು: • TryGetValue + indexer: 160.0 ms • GetValueRefOrAddDefault: 95.0 ms

ಒಂದೇ ಒಂದು ಲುಕ್‌ಅಪ್ ಇರುವ ಆವೃತ್ತಿಯು 1.7x ವೇಗವಾಗಿದೆ.

ಮುಖ್ಯವಾಗಿ, ಮೆಮೊರಿ ಅಲೋಕೇಶನ್‌ಗಳು (memory allocations) ಬದಲಾಗದೆ ಒಂದೇ ರೀತಿಯಲ್ಲಿವೆ. ಈ ತಂತ್ರವು ಮೆಮೊರಿಯನ್ನು ಉಳಿಸುವುದಿಲ್ಲ. ಇದು ಕೇವಲ CPU ಅನ್ನು ಉಳಿಸುತ್ತದೆ. ನಿಮ್ಮ ಕೋಡ್ ಗಾರ್ಬೇಜ್ ಕಲೆಕ್ಷನ್ (garbage collection) ಕಾರಣದಿಂದ ನಿಧಾನವಾಗಿದ್ದರೆ, ಈ ಬದಲಾವಣೆ ಏನನ್ನೂ ಮಾಡುವುದಿಲ್ಲ. ನಿಮ್ಮ ಕೋಡ್ ಹೆಚ್ಚಿನ ಎಣಿಕೆಯ (heavy counting) ಕಾರಣದಿಂದ ನಿಧಾನವಾಗಿದ್ದರೆ, ಇದು ಸಹಾಯ ಮಾಡುತ್ತದೆ.

ನಿಮ್ಮ ಲೂಪ್ ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಕೀಗಳಿಗೆ ಅನೇಕ ಅಪ್‌ಡೇಟ್‌ಗಳನ್ನು ಮಾಡುವಾಗ ಇದನ್ನು ಬಳಸಿ. ಅಪ್‌ಡೇಟ್‌ಗಳಿಗೂ ಇನ್‌ಸರ್ಟ್‌ಗಳಿಗೂ (inserts) ಇರುವ ಅನುಪಾತ ಹೆಚ್ಚಾದಂತೆ ಇದರ ಪ್ರಯೋಜನವೂ ಹೆಚ್ಚಾಗುತ್ತದೆ.

ಎಚ್ಚರಿಕೆ: ಈ ರೆಫರೆನ್ಸ್ ಡಿಕ್ಷನರಿಯ ಆಂತರಿಕ ಸ್ಟೋರೇಜ್ ಅನ್ನು ಸೂಚಿಸುತ್ತದೆ. ಇದು ಮುಂದಿನ ರಚನಾತ್ಮಕ ಬದಲಾವಣೆ (structural change) ಆಗುವವರೆಗೆ ಮಾತ್ರ ಮಾನ್ಯವಾಗಿರುತ್ತದೆ. ನೀವು ಕೀಗಳನ್ನು ಸೇರಿಸಿದರೆ ಅಥವಾ ತೆಗೆದುಹಾಕಿದರೆ ರೆಫರೆನ್ಸ್ ಅನ್ನು ಹಿಡಿದಿಟ್ಟುಕೊಳ್ಳಬೇಡಿ. ರೆಫರೆನ್ಸ್ ಅನ್ನು ಪಡೆದುಕೊಳ್ಳಿ, ಮೌಲ್ಯವನ್ನು ಬದಲಾಯಿಸಿ ಮತ್ತು ಮುಂದುವರಿಯಿರಿ.

ಹೆಚ್ಚಿನ ಕೆಲಸಗಳಿಗೆ TryGetValue ಬಳಸಿ. ಇದು ಓದಲು ಸುಲಭವಾಗಿದೆ. ನಿಮ್ಮ ಡಿಕ್ಷನರಿ ಲೂಪ್ ಪರ್ಫಾರ್ಮೆನ್ಸ್ ಬಾಟಲ್ನೆಕ್ (performance bottleneck) ಆಗಿದ್ದಾಗ ಮಾತ್ರ ref ಆವೃತ್ತಿಯನ್ನು ಬಳಸಿ.

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