7.8 MB ਦੀਆਂ Keys ਜੋ ਮੈਂ ਸਿਰਫ਼ ਸੁੱਟਣ ਲਈ ਅਲਾਕੋਕੇਟ ਕੀਤੀਆਂ ਸਨ

ਇੱਕ ਪ੍ਰੋਫਾਈਲਰ (profiler) ਨੇ ਮੇਰੇ ਕੋਡ ਵਿੱਚ ਇੱਕ ਲੁਕੀ ਹੋਈ ਲਾਗਤ (hidden 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) ਵਰਤਦੇ ਹੋ ਜਿਸ ਵਿੱਚ alternate lookup ਸਪੋਰਟ ਨਹੀਂ ਹੈ, ਤਾਂ ਇਹ runtime 'ਤੇ error ਸੁੱਟੇਗਾ।
  • ਪਾਰਸਰ (parsers), ਲੌਗ ਪ੍ਰੋਸੈਸਰ (log processors), ਜਾਂ CSV ਸਕੈਨਰਾਂ ਵਰਗੇ 'hot paths' ਲਈ ਆਦਰਸ਼ ਹੈ।
  • ਸਿਰਫ਼ ਕੁਝ ਹੀ ਕੀਜ਼ ਵਾਲੇ ਸਧਾਰਨ ਲੁੱਕਅੱਪ ਲਈ ਇਸਨੂੰ ਛੱਡ ਦਿਓ।

ਹੁਣ ਮੈਂ ਆਪਣੇ ਕੋਡ ਵਿੱਚ Substring ਦੇ ਨਾਲ TryGetValue ਦੇ ਪੈਟਰਨ ਨੂੰ ਲੱਭਦਾ ਹਾਂ। ਉਹ ਪੈਟਰਨ ਮੈਮੋਰੀ ਦੀ ਬਹੁਤ ਜ਼ਿਆਦਾ ਵਰਤੋਂ (waste) ਕਰਦਾ ਹੈ।

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