Advertisement

How Smelly Is it? (c# UDP Socket Class for multiplayer game)

Started by October 12, 2018 01:26 AM
17 comments, last by Septopus 6 years, 1 month ago

I can't figure out how to quote just one small section of your code block, so...

The section where you use messageDictionary.ContainsKey, Add, then [ ] operators can be condensed into a slightly more efficient form:


Queue<string> queue;
if (!messageDictionary.TryGetValue(so.epFrom, out queue)
{
    messageDictionary.Add(so.epFrom, queue = new Queue<string>());
}
queue.Enqueue(so.message);

This is a pretty common pattern in C#.

If you're using newer versions of C#, you can also say "out var queue" instead of defining the variable above TryGetValue.

Yes!  haha, somehow I completely forgot about TryGetValue, and I've made some fairly extensive use of Dictionaries in the rest of the code... Adding this to my growing list of optimizations to rewrite later!

Thanks again!! :D

Advertisement

if (!messageDictionary.ContainsKey(so.epFrom))
{
	messageDictionary.Add(so.epFrom, new Queue<string> ());
}
messageDictionary[so.epFrom].Enqueue(so.smessage);

Okay, so here's the code we're talking about and just to make sure I understand Here's how it looks with TryGetValue:


Queue<string> queue;
if (!messageDictionary.TryGetValue(so.epFrom, out queue))
{
	messageDictionary.Add(so.epFrom, queue = new Queue<string>());
}
queue.Enqueue(so.smessage);

That seems right..

But, doesn't this create an extra pointer or something like that for the local queue variable?  I always have to ask why, when an optimization ends up taking up more lines of code. ;)

I'm not sure, but I think in THIS specific case I'm going to keep it as is.

However, the reminder about TryGetValue will probably be priceless when I hit the optimize button. ;)

No, you'll need a queue pointer no matter what. And pointers themselves are value types, not causing garbage collection. (The pointed-at things of course are probably not value types.)

enum Bool { True, False, FileNotFound };

So, then is the second option actually more optimized?  It still seems like there is an extra reference that otherwise isn't needed...  But that's just nit-picking as far as I'm concerned...

My new question is, does TryGetValue actually cost LESS than ContainsKey?  I don't see how it could...

I think that's the biggest question for me in THIS scenario...

Never mind.. ;)

https://stackoverflow.com/questions/9382681/what-is-more-efficient-dictionary-trygetvalue-or-containskeyitem

This may be a better one:

https://stackoverflow.com/questions/39885203/is-there-a-reason-why-one-should-use-containskey-over-trygetvalue/39885383

But basically what I'm taking from this is since I'm accessing the queue by it's Index in the Dictionary (in the first method) it's duplicating the lookup that it's already performed with ContainsKey.

So in the second method the "extra" pointer is actually avoiding a second lookup into the Dictionary.. yes?

Sweet!  You people are wicked smart!

;)

Advertisement

Basically the reason to make this specific optimization is that if you do both a ContainsKey and also an indexer (dictionary[key]) operation, the code has to find the entry (or insertion point) in the dictionary twice.  Although the entry lookup is amortized constant time (one GetHashCode call and usually one Equals call), why do it twice if you only need to do it once?

If you find yourself in a chunk of code that only needs to call ContainsKey and nothing else, keep using ContainsKey.

One neat thing you can use to judge complexity of built-in .Net code is to look at the reference source:

https://referencesource.microsoft.com/#mscorlib/system/collections/generic/dictionary.cs

While it might not always be exactly what ships in .Net, it should be close enough that you can base optimization decisions on it.

Same thing I realized after reading some other internet posts on the topic!

Thanks for all the Info!!

This topic is closed to new replies.

Advertisement