Register for your free account! | Forgot your password?

Go Back   elitepvpers > MMORPGs > Conquer Online 2 > CO2 Private Server > CO2 PServer Guides & Releases
You last visited: Today at 06:17

  • Please register to post and access all features, it's quick, easy and FREE!

Advertisement



Selector - Multikeyed concurrent dictionary(dictionaries)

Discussion on Selector - Multikeyed concurrent dictionary(dictionaries) within the CO2 PServer Guides & Releases forum part of the CO2 Private Server category.

Reply
 
Old   #1
 
Super Aids's Avatar
 
elite*gold: 0
Join Date: Dec 2012
Posts: 1,761
Received Thanks: 950
Selector - Multikeyed concurrent dictionary(dictionaries)

Namespaces Required:
Code:
using System.Collections.Concurrent;
Class:
Code:
	public class Selector<TKey1, TKey2, TValue>
	{
		private ConcurrentDictionary<TKey1, TValue> selectorCollection1;
		private ConcurrentDictionary<TKey2, TValue> selectorCollection2;
		private ConcurrentDictionary<TKey1, TKey2> keymatcher1;
		private ConcurrentDictionary<TKey2, TKey1> keymatcher2;

		public Selector()
		{
			selectorCollection1 = new ConcurrentDictionary<TKey1, TValue>();
			selectorCollection2 = new ConcurrentDictionary<TKey2, TValue>();

			keymatcher1 = new ConcurrentDictionary<TKey1, TKey2>();
			keymatcher2 = new ConcurrentDictionary<TKey2, TKey1>();
		}

		public bool TrySelect(TKey1 key, out TValue value)
		{
			return selectorCollection1.TryGetValue(key, out value);
		}

		public bool TrySelect(TKey2 key, out TValue value)
		{
			return selectorCollection2.TryGetValue(key, out value);
		}

		public bool TryAdd(TKey1 key1, TKey2 key2, TValue value)
		{
			return (selectorCollection1.TryAdd(key1, value) &&
			        selectorCollection2.TryAdd(key2, value) &&
			        keymatcher1.TryAdd(key1, key2) &&
			        keymatcher2.TryAdd(key2, key1));
		}

		public bool TryRemove(TKey1 key1)
		{
			TKey2 key2;
			if (!keymatcher1.TryGetValue(key1, out key2))
				return false;
			TValue rval;
			return (selectorCollection1.TryRemove(key1, out rval) &&
			        selectorCollection2.TryRemove(key2, out rval) &&
			        keymatcher1.TryRemove(key1, out key2) &&
			        keymatcher2.TryRemove(key2, out key1));
		}

		public bool TryRemove(TKey2 key2)
		{
			TKey1 key1;
			if (!keymatcher2.TryGetValue(key2, out key1))
				return false;
			TValue rval;
			return (selectorCollection1.TryRemove(key1, out rval) &&
			        selectorCollection2.TryRemove(key2, out rval) &&
			        keymatcher1.TryRemove(key1, out key2) &&
			        keymatcher2.TryRemove(key2, out key1));
		}

		public bool Contains(TKey1 key1)
		{
			return keymatcher1.ContainsKey(key1);
		}

		public bool Contains(TKey2 key2)
		{
			return keymatcher2.ContainsKey(key2);
		}

		public bool Contains(TValue value)
		{
			return selectorCollection1.Values.Contains(value);
		}
		
		public void Clear()
		{
				selectorCollection1.Clear();
				selectorCollection2.Clear();
				keymatcher1.Clear();
				keymatcher2.Clear();
		}
	}
Example if using the selector to store clients:
Code:
Selector<uint, string, GameClient> Clients; // uint is for the UID, string is for the name and GameClient is the client...
Adding a client:
Code:
			if (!Clients.TryAdd(client.UID, client.Name, client))
				throw new Exception("Could not add the client.");
Getting a client from UID:
Code:
	if (!Clients.TrySelect(1000000, out client)) // get from UID
				throw new Exception("Could not get the client from UID.");
Getting a client from name:
Code:
if (!Clients.TrySelect("Test", out client)) // get from name
				throw new Exception("Could not get the client from name.");
Contains a client from an uid:
Code:
			if (Clients.Contains(1000000))
			{
				// a client with the uid 1000000 is existing
			}
Contains a client from a name:
Code:
		if (Clients.Contains("Test"))
			{
				// a client with the name Test is existing
			}
Contains a client from well... client.
Code:
			if (Clients.Contains(client))
			{
				// the selector already contains the client
			}
Remove a client from UID:
Code:
			int tries = 0;
			while (!Clients.TryRemove(1000000) && (tries++ < 3)) // attempts to remove a client from the uid 1000000
				System.Threading.Thread.Sleep(100); // the while loop is to make sure it will try to remove it again if it failed, most likely never will
Remove a client from name: (Please read the commenting at the UID removing)
Code:
int tries = 0;
			while (!Clients.TryRemove("Test") && (tries++ < 3)) // attempts to remove a client from the name Test
				System.Threading.Thread.Sleep(100);
Clear the collection:
Code:
Clients.Clear();
Never seen anything like this being used a single source, although it would be common to have something like that and I was in need for it in my source, so wrapped it up pretty quickly, but I hope you like it.

And pro4never said he wanted some test on it to see if it was worth it.

Well I just went ahead and did a test, here is the result:
A bit surprised about the concurrent Dictionary, because I didn't though it'd be that slow compared to a regular dictionary o:

If you ask me it's worth it lmao.

Screenshot:


Test:
Code:
			#region Regular Dictionary Test
			System.Collections.Generic.Dictionary<uint, GameClient> d_clients = new System.Collections.Generic.Dictionary<uint, Program.GameClient>();
			foreach (GameClient _client in clients)
				d_clients.Add(_client.UID, _client);
			Console.WriteLine("Beginning first test: System.Collections.Generic.Dictionary<TKey, TValue>");
			Console.WriteLine("Started with 1000000 searches (Running 3 tests)...");
			for (int tests = 0; tests < 3; tests++)
			{
				Stopwatch overall = new Stopwatch();
				overall.Start();
				int namecounter = 0;
				for (int runs = 0; runs < 1000000; runs++)
				{
					string names = namesearches[namecounter];
					foreach (GameClient client in d_clients.Values)
					{
						if (client.Name == names)
							break;
					}
					
					namecounter++;
					if (namecounter >= 500)
						namecounter = 0;
				}
				Console.WriteLine("Finished test #{0} in {1} milliseconds...", tests, overall.ElapsedMilliseconds);
				overall.Stop();
			}
			#endregion
			#region Concurrent Dictionary Test
			ConcurrentDictionary<uint, GameClient> c_clients = new ConcurrentDictionary<uint, Program.GameClient>();
			foreach (GameClient _client in clients)
				c_clients.TryAdd(_client.UID, _client);
			Console.WriteLine("Beginning first test: System.Collections.Concurrent.Dictionary<TKey, TValue>");
			Console.WriteLine("Started with 1000000 searches (Running 3 tests)...");
			for (int tests = 0; tests < 3; tests++)
			{
				Stopwatch overall = new Stopwatch();
				overall.Start();
				int namecounter = 0;
				for (int runs = 0; runs < 1000000; runs++)
				{
					string names = namesearches[namecounter];
					foreach (GameClient client in c_clients.Values)
					{
						if (client.Name == names)
							break;
					}
					
					namecounter++;
					if (namecounter >= 500)
						namecounter = 0;
				}
				Console.WriteLine("Finished test #{0} in {1} milliseconds...", tests, overall.ElapsedMilliseconds);
				overall.Stop();
			}
			#endregion
			#region Selector Dictionary Test
			Selector<uint, string, GameClient> s_clients = new Selector<uint, string, Program.GameClient>();
			foreach (GameClient _client in clients)
				s_clients.TryAdd(_client.UID, _client.Name, _client);
			Console.WriteLine("Beginning first test: Selector<TKey1, TKey2, TValue>");
			Console.WriteLine("Started with 1000000 searches (Running 3 tests)...");
			for (int tests = 0; tests < 3; tests++)
			{
				Stopwatch overall = new Stopwatch();
				overall.Start();
				int namecounter = 0;
				int fails = 0;
				for (int runs = 0; runs < 1000000; runs++)
				{
					string names = namesearches[namecounter];
					GameClient client;
					if (!s_clients.TrySelect(names, out client))
						fails++;
					
					namecounter++;
					if (namecounter >= 500)
						namecounter = 0;
				}
				Console.WriteLine("Selector fails: {0}", fails);
				Console.WriteLine("Finished test #{0} in {1} milliseconds...", tests, overall.ElapsedMilliseconds);
				overall.Stop();
			}
			#endregion
Super Aids is offline  
Thanks
3 Users
Old 01/31/2013, 08:01   #2
 
Lateralus's Avatar
 
elite*gold: 0
Join Date: May 2005
Posts: 1,892
Received Thanks: 920
Hey cool. I was wanting to do something like this to implement a thread-safe priority queue a while back.
Lateralus is offline  
Old 01/31/2013, 12:21   #3


 
Korvacs's Avatar
 
elite*gold: 20
Join Date: Mar 2006
Posts: 6,126
Received Thanks: 2,518
Just so your aware, the concurrent dictionary is slower than a standard dictionary because its designed to be used by 10/20+ threads at the same time without blocking, not a single thread.

Also, your tests between Concurrent Dictionary and this Selector should yield the same results, but you test them differently, that's the only reason there is a performance difference. Personally I don't see the point.
Korvacs is offline  
Old 01/31/2013, 12:45   #4


 
Korvacs's Avatar
 
elite*gold: 20
Join Date: Mar 2006
Posts: 6,126
Received Thanks: 2,518
Oh and you don't need to lock to clear the dictionaries, and you should never lock(this) its extremely bad practice and can cause deadlocks.
Korvacs is offline  
Old 01/31/2013, 13:01   #5
 
Super Aids's Avatar
 
elite*gold: 0
Join Date: Dec 2012
Posts: 1,761
Received Thanks: 950
Quote:
Originally Posted by Korvacs View Post
Just so your aware, the concurrent dictionary is slower than a standard dictionary because its designed to be used by 10/20+ threads at the same time without blocking, not a single thread.

Also, your tests between Concurrent Dictionary and this Selector should yield the same results, but you test them differently, that's the only reason there is a performance difference. Personally I don't see the point.
The point is usually you store you clients with the UID being the key and when you have to find a player from the name you'd have to search through the whole collection to find that certain player ex. from whispering someone. That's when having a secondary collection with the name as the key comes in hand, because you wouldn't need to search through the whole collection to find the client you want to use.

It's not really a performance test, but speed test. The tests is the same for all 3 types of dictionaries and I was aware it was the reason it was slower, just didn't know it was that much. The only difference is that mine does not need to loop through the whole collection to find what it's looking for as it contains two collections one for the UID being the key and another one for being the name as key, so It would just have to call the collection that uses the name as key, rather than looping through the UID collection.

All in all it would be equal to:
Code:
ConcurrentDictionary<uint, GameClient> ClientsWithUIDKey;
ConcurrentDictionary<string, GameClient> ClientsWithNameKey;
So, nothing more than a wrapper for that.

Quote:
Originally Posted by Korvacs View Post
Oh and you don't need to lock to clear the dictionaries, and you should never lock(this) its extremely bad practice and can cause deadlocks.
Thanks
Super Aids is offline  
Thanks
1 User
Old 01/31/2013, 13:07   #6


 
Korvacs's Avatar
 
elite*gold: 20
Join Date: Mar 2006
Posts: 6,126
Received Thanks: 2,518
Can you create a test to see how performance is at inserting between the 3? To be honest the performance between the 2 when searching is minimal when you only have 1000 or less entries in the dictionary, so much so that this isn't really worth it, and I suspect inserting into 4 dictionaries instead of 1 is going to have a bigger impact...
Korvacs is offline  
Old 01/31/2013, 13:10   #7
 
Super Aids's Avatar
 
elite*gold: 0
Join Date: Dec 2012
Posts: 1,761
Received Thanks: 950
This test was done with 500 entries only.

And I'm aware inserting would be slower, same with removing, but you only do those once. Uhmm will do it now and edit my post.

Alright here is the insert/removal test result:


Test:
Code:
			#region Regular Dictionary Test
			System.Collections.Generic.Dictionary<uint, GameClient> d_clients = new System.Collections.Generic.Dictionary<uint, Program.GameClient>();
			Console.WriteLine("Beginning first test: System.Collections.Generic.Dictionary<TKey, TValue>");
			Console.WriteLine("Started with 500 inserts/removals (Running 10 tests)...");
			for (int tests = 0; tests < 10; tests++)
			{
				Stopwatch overall = new Stopwatch();
				overall.Start();
				for (int runs = 0; runs < 500; runs++)
				{
					d_clients.Add((uint)runs, new GameClient() { Name = "Test" });
				}
				for (int runs = 0; runs < 500; runs++)
				{
					d_clients.Remove((uint)runs);
				}
				Console.WriteLine("Finished test #{0} in {1} ticks...", tests, overall.ElapsedTicks);
				d_clients.Clear();
				overall.Stop();
			}
			#endregion
			#region Concurrent Dictionary Test
			ConcurrentDictionary<uint, GameClient> c_clients = new ConcurrentDictionary<uint, Program.GameClient>();
			Console.WriteLine("Beginning first test: System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>");
			Console.WriteLine("Started with 500 inserts/removals (Running 10 tests)...");
			for (int tests = 0; tests < 10; tests++)
			{
				Stopwatch overall = new Stopwatch();
				overall.Start();
				for (int runs = 0; runs < 500; runs++)
				{
					c_clients.TryAdd((uint)runs, new GameClient() { Name = "Test" });
				}
				for (int runs = 0; runs < 500; runs++)
				{
					GameClient client;
					c_clients.TryRemove((uint)runs, out client);
				}
				Console.WriteLine("Finished test #{0} in {1} ticks...", tests, overall.ElapsedTicks);
				c_clients.Clear();
				overall.Stop();
			}
			#endregion
			#region Selector Dictionary Test
			Selector<uint, string, GameClient> s_clients = new Selector<uint, string, Program.GameClient>();
			Console.WriteLine("Beginning first test: Selector<TKey1, TKey2, TValue>");
			Console.WriteLine("Started with 500 inserts/removals (Running 10 tests)...");
			for (int tests = 0; tests < 10; tests++)
			{
				Stopwatch overall = new Stopwatch();
				overall.Start();
				for (int runs = 0; runs < 500; runs++)
				{
					GameClient client = new Program.GameClient();
					client.Name = "Test";
					s_clients.TryAdd((uint)runs, client.Name, client);
				}
				for (int runs = 0; runs < 500; runs++)
				{
					s_clients.TryRemove((uint)runs);
				}
				Console.WriteLine("Finished test #{0} in {1} ticks...", tests, overall.ElapsedTicks);
				s_clients.Clear();
				overall.Stop();
			}
			#endregion
Also if you know why first test could possibly be so much slower than the rest even though all tests are the same, an explanation would be nice, because it's a bit confusing to me
Super Aids is offline  
Old 01/31/2013, 17:26   #8


 
Korvacs's Avatar
 
elite*gold: 20
Join Date: Mar 2006
Posts: 6,126
Received Thanks: 2,518
Its because the first time you run the application after compiling every call needs to be JIT'ed, then after that it'll run as intended.
Korvacs is offline  
Old 01/31/2013, 18:52   #9
 
Super Aids's Avatar
 
elite*gold: 0
Join Date: Dec 2012
Posts: 1,761
Received Thanks: 950
Quote:
Originally Posted by Korvacs View Post
Its because the first time you run the application after compiling every call needs to be JIT'ed, then after that it'll run as intended.
But why does it repeat it then? Shouldn't it be doing it for the first one only?
Super Aids is offline  
Old 01/31/2013, 21:36   #10


 
Korvacs's Avatar
 
elite*gold: 20
Join Date: Mar 2006
Posts: 6,126
Received Thanks: 2,518
Its probably to do with the fact that it is being called within a loop, I couldn't tell you exactly why I'm not overly knowledgeable about JIT.
Korvacs is offline  
Old 01/31/2013, 21:47   #11
 
Super Aids's Avatar
 
elite*gold: 0
Join Date: Dec 2012
Posts: 1,761
Received Thanks: 950
I know. I was thinking something like that. I just find it strange that it is like that, but it's not anything that its noticable though.
Super Aids is offline  
Old 01/31/2013, 22:20   #12
 
elite*gold: 0
Join Date: Jun 2009
Posts: 787
Received Thanks: 314
Quote:
Originally Posted by Super Aids View Post
. . .

Test:
Code:
			#region Regular Dictionary Test
			System.Collections.Generic.Dictionary<uint, GameClient> d_clients = new System.Collections.Generic.Dictionary<uint, Program.GameClient>();
			Console.WriteLine("Beginning first test: System.Collections.Generic.Dictionary<TKey, TValue>");
			Console.WriteLine("Started with 500 inserts/removals (Running 10 tests)...");
			for (int tests = 0; tests < 10; tests++)
			{
				Stopwatch overall = new Stopwatch();
				overall.Start();
				for (int runs = 0; runs < 500; runs++)
				{
					d_clients.Add((uint)runs, new GameClient() { Name = "Test" });
				}
				for (int runs = 0; runs < 500; runs++)
				{
					d_clients.Remove((uint)runs);
				}
				Console.WriteLine("Finished test #{0} in {1} ticks...", tests, overall.ElapsedTicks);
				d_clients.Clear();
				overall.Stop();
			}
			#endregion
			#region Concurrent Dictionary Test
			ConcurrentDictionary<uint, GameClient> c_clients = new ConcurrentDictionary<uint, Program.GameClient>();
			Console.WriteLine("Beginning first test: System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>");
			Console.WriteLine("Started with 500 inserts/removals (Running 10 tests)...");
			for (int tests = 0; tests < 10; tests++)
			{
				Stopwatch overall = new Stopwatch();
				overall.Start();
				for (int runs = 0; runs < 500; runs++)
				{
					c_clients.TryAdd((uint)runs, new GameClient() { Name = "Test" });
				}
				for (int runs = 0; runs < 500; runs++)
				{
					GameClient client;
					c_clients.TryRemove((uint)runs, out client);
				}
				Console.WriteLine("Finished test #{0} in {1} ticks...", tests, overall.ElapsedTicks);
				c_clients.Clear();
				overall.Stop();
			}
			#endregion
			#region Selector Dictionary Test
			Selector<uint, string, GameClient> s_clients = new Selector<uint, string, Program.GameClient>();
			Console.WriteLine("Beginning first test: Selector<TKey1, TKey2, TValue>");
			Console.WriteLine("Started with 500 inserts/removals (Running 10 tests)...");
			for (int tests = 0; tests < 10; tests++)
			{
				Stopwatch overall = new Stopwatch();
				overall.Start();
				for (int runs = 0; runs < 500; runs++)
				{
					GameClient client = new Program.GameClient();
					client.Name = "Test";
					s_clients.TryAdd((uint)runs, client.Name, client);
				}
				for (int runs = 0; runs < 500; runs++)
				{
					s_clients.TryRemove((uint)runs);
				}
				Console.WriteLine("Finished test #{0} in {1} ticks...", tests, overall.ElapsedTicks);
				s_clients.Clear();
				overall.Stop();
			}
			#endregion
Also if you know why first test could possibly be so much slower than the rest even though all tests are the same, an explanation would be nice, because it's a bit confusing to me
JIT considerations aside, you are using the same dictionary for every iteration of the test. Dictionaries are backed by an array, so when you add enough elements the collection has to expand and thus create more memory. Creating memory and copying the old elements to the new array takes linear time, and thus slows down a (generally) constant time process. Although you remove all the entries and clear the dictionary, the expanded array is still there (see ms reference for clear, it's an O(n) operation meaning they don't simply create a new array of initial size - ). The subsequent tests thus never need to expand the array and run without that bottleneck.

You should rerun the tests with new dictionary instances for each iteration.
_tao4229_ is offline  
Thanks
1 User
Old 01/31/2013, 23:11   #13
 
elite*gold: 21
Join Date: Jul 2005
Posts: 9,193
Received Thanks: 5,380
Quote:
Originally Posted by _tao4229_ View Post
JIT considerations aside, you are using the same dictionary for every iteration of the test. Dictionaries are backed by an array, so when you add enough elements the collection has to expand and thus create more memory. Creating memory and copying the old elements to the new array takes linear time, and thus slows down a (generally) constant time process. Although you remove all the entries and clear the dictionary, the expanded array is still there (see ms reference for clear, it's an O(n) operation meaning they don't simply create a new array of initial size - ). The subsequent tests thus never need to expand the array and run without that bottleneck.

You should rerun the tests with new dictionary instances for each iteration.
Yay thanks for the clarification. Now I feel kinda smart for remembering how the array still exists behind the scenes but couldn't think of how to explain it or even if I was correct.
pro4never is offline  
Thanks
1 User
Old 01/31/2013, 23:12   #14
 
Spirited's Avatar
 
elite*gold: 12
Join Date: Jul 2011
Posts: 8,283
Received Thanks: 4,192
Why not just use a KeyValuePair<T1, T2>? .-.
Spirited is offline  
Old 02/01/2013, 00:07   #15
 
Super Aids's Avatar
 
elite*gold: 0
Join Date: Dec 2012
Posts: 1,761
Received Thanks: 950
Fang the point is to not needing 2 keys to be correct to get the value, but just one or the other.

@tao thanks a lot for the explanation.
Super Aids is offline  
Reply


Similar Threads Similar Threads
Für Neulinge in WoW: WoW Slang Dictionary als App
04/21/2012 - World of Warcraft - 3 Replies
Hi, ich möchte denen, die neu in WoW sind und mit Sachen im /2 wie "retri pala lfg fl10 nhc" oder mit Begriffen wie "AoE oder Proc" nichts anzufangen wissen, mal eine App empfehlen, die mir schon oft geholfen hat: "WoW Slang Dictionary" für Android ist eine nette Anwendung, in der nach den Begriffen aus der WoW Sprache gesucht werden kann und die gut erklärt werden. Die App ist auf (easy) Englisch, es sollte deshalb kein Problem sein. LG :)
Thread Safety? Dictionary Locking - I need Help.
10/10/2011 - CO2 Programming - 19 Replies
Well, locking a dictionary seems to be not that clear. Monitor.Enter(AllMobs); { foreach (KeyValuePair<uint, Mob> kvp in AllMobs) { Mob m = kvp.Value; m.GetTarget(); } }
[08:58:34 م] [GA]Skilton: No such file or dictionary . need help please
08/24/2011 - Metin2 Private Server - 0 Replies
Hi I'm having probelm after setting up the game . I'm trying to connect to the server from the client but it appears this problem ( No such file or dictionary ) . Does anyone know how to solve this problem ? Thanks in advance . http://www14.0zz0.com/2011/08/24/17/781900522.jpg
Special Force Dictionary... Just For Fun
05/02/2010 - Soldier Front Philippines - 33 Replies
This is the first ever SF dictionary. I hope it will help most of you. xD:p:p AFK Meaning: Away From Keyboard. Aimbot Meaning: A cheat where you automatically hit the enemy whether you really missed it or not. ~thnx †khûLèt.ø26™† Aw Meaning: To Express pain; exclamation of pain.. Usually said when someone dies. Most common SF word ~thnx
RIESEN MULTIHACK SELECTOR PROBLEM BEI JEDEN SELECTOR
01/01/2009 - Metin2 - 10 Replies
Also hallo erstmal ... ja ich will sofort zur Sache kommen wie die Überschrift sagt hab ich ein Riesen Problem mit jeden Multihack : Ich versuche schon sehnend Hacks anzumachen aba bei jeden klappts net MHS:1.7 = Geht garnet mehr wegen neues Update von Metin2 -2.7 habe die Hilfsdatei geholt , .net dingen geholt , ja und den Multihack und es tretet ein Fehler auf nämlich :MSCOMCTL.OCX hab sogar die Datei repariert klappt nicht ja und die anderen hacks klappen auch genauso nicht Kann...



All times are GMT +1. The time now is 06:18.


Powered by vBulletin®
Copyright ©2000 - 2026, Jelsoft Enterprises Ltd.
SEO by vBSEO ©2011, Crawlability, Inc.
This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

Support | Contact Us | FAQ | Advertising | Privacy Policy | Terms of Service | Abuse
Copyright ©2026 elitepvpers All Rights Reserved.