0
\$\begingroup\$

I wonder if it is wise to use ConcurrentDictionary as a main container in a game? That is it will be used to:

  • update every object inside it in a parallel.Foreach loop;

  • add (but not remove) objects inside it in a parallel.Foreach loop (sometimes while updating);

  • retreive data from the dictionary with a Linq query/ simply by index in sometimes a parallel loop, but mostly in the main thread;

  • remove data from it in a parallel loop (but NOT when updating objects) <- this step can be done in the main thread.

What could be the disadvantages of this list compared to HashSet (we are using the latter now, but we are only reading from it in parallel - when updating models). What lists are used in big engines?

\$\endgroup\$
  • \$\begingroup\$ From the times I've had to really optimize code if performance is enough of an issue that having optimally concurrent insertions and removals into a collection is impactful the number of GC calls is going to be a massive issue unless you have a huge memory allowance. \$\endgroup\$ – ClassicThunder Oct 9 '14 at 18:58
  • \$\begingroup\$ can't clearly understand your post without punctuation, what are GC calls? And why would they be a massive issue? \$\endgroup\$ – cubrman Oct 9 '14 at 19:05
  • \$\begingroup\$ You are constantly creating and destroying things. This creates garbage. The Garbage Collector (GC) spends time freeing this space. Creating and destroying things more quickly typically doesn't help performance compared improving your memory management so that the GC called less. You might want to look into pools or something similar. \$\endgroup\$ – ClassicThunder Oct 9 '14 at 19:51
  • \$\begingroup\$ @ClassicThunder oh now I got it. I am already using pools, so I am not creating / deleting stuff I merely reset and move it around :). The question was about ConcurentDictionary's performance in concurrent read/write/remove/add situations. \$\endgroup\$ – cubrman Oct 10 '14 at 6:00
1
\$\begingroup\$

I wouldn't expect significant performance disadvantages to using a ConcurrentDictionary in your case, but as with anything concurrent I'd recommend tests/profiling.

Also, I haven't the foggiest idea what the big engines are using. Sorry.

That said, let's check your 4 uses against the information here: http://arbel.net/2013/02/03/best-practices-for-using-concurrentdictionary/ (Interesting read, esp. for cautionary notes.)

  1. Updating should be no different, though you'll want to think about whether it's an issue for you that the enumerator can change during the loop. (The enumerators aren't snapshots.)
  2. You may need some bookkeeping to control updates on mid-update-loop additions. It seems like these additions won't cause significant contention thanks to the multiple-lock design, but you'll want to verify that for yourself.
  3. Retrieving "by index" seems a little fishy to me, depending on what exactly you have in mind. Linq seems fine.
  4. Removing should be fine, though again the enumerator can change so be careful with the parallel loop.

It seems to me that your biggest risk in using a ConcurrentDictionary is, unsurprisingly, bookkeeping/correctness. That comes with the territory, especially if you're shifting back and forth between distributed and centralized (main thread) logic.

As long as you carefully compare your usage/assumptions against what the CD is doing, the change should yield a solid scalability win over your HashSet implementation. Considering the additional complexity, though, if you haven't yet verified that the HashSet is a significant bottleneck, you may want to do that first.

\$\endgroup\$
  • \$\begingroup\$ I did see the article before, thanks. To be honest the biggest bottleneck is updating the models and it is already done in a parallel loop (HashSet supports concurrent reading no problem). The only thing I am considering is whether it is worth switching to concurrent dictionary to be able to add/remove elements wherever I please, but on the second thought, I guess it is not so safe of an idea :). But what is that scalability you were talking about? Does it mean that concurrent reading from a CD is faster than from HashSet? \$\endgroup\$ – cubrman Oct 9 '14 at 19:08
  • 1
    \$\begingroup\$ Not to my knowledge; I think they're roughly identical reads. I just meant the ability to do scale by doing add/removes in parallel. I assumed you had some kind of full lock (explicit or implicit) over your HashSet for writes, so you'd get away from that. It isn't necessarily un-safe to do them concurrently, just needs to be designed carefully around. Sorry I couldn't be more help. :) \$\endgroup\$ – Chris Mills-Price Oct 9 '14 at 20:11
  • \$\begingroup\$ For now, I have a ConcurrentBag which I add into in parallel and then copy elements into main HashSet in the main thread. The deletion is also centralized. I guess if the reading speed is the same it's not worth it to overcomplicate the system. Thanks! \$\endgroup\$ – cubrman Oct 10 '14 at 5:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.