28 Jul, 2011, Mastermosley wrote in the 1st comment:
Votes: 0
Just wondering what you guys figure is a better model for the base server of a mud, a Synchronous Thread per connection socket or Asynchronous Sockets?
28 Jul, 2011, Runter wrote in the 2nd comment:
Votes: 0
Not a thread per connection.
28 Jul, 2011, Twisol wrote in the 3rd comment:
Votes: 0
Probably not a thread per connection. Almost everything a player does effects or accesses shared state, so it would be needless overhead. I -would- keep a thread pool for deferred actions and things that take a while, but async IO should be fine for the actual player connections.
28 Jul, 2011, Mastermosley wrote in the 4th comment:
Votes: 0
Twisol said:
Probably not a thread per connection. Almost everything a player does effects or accesses shared state, so it would be needless overhead. I -would- keep a thread pool for deferred actions and things that take a while, but async IO should be fine for the actual player connections.


Okay, that was my assumption as well. I have tested each model, Simple Synchronous Thread Per Connection, Reactor and Proactor and noticed no difference in processing time but it proboboly wouldn't be noticeable with less than 100 connections. I have also read about a newer model that boasts better performance then all of them, TProactor. Does anyone have any experience with this in a mud server? Does it actually work better then the other models?
28 Jul, 2011, Kaz wrote in the 5th comment:
Votes: 0
As my application is currently single-threaded, and does a lot of data manipulation I've been considering these questions myself. Here's the general structure I've come up with:

* Given two types of parallelism that you can use: task parallelism, where you can perform different activities independently from each other, and data parallelism, where you can perform many parts of the same activity concurrently:

1) It's possible to make the game world multi-threaded, but it's difficult to come up with a good locking scheme. Therefore, I would suggest changing the algorithms to favour data parallelism, with special cases for task parallelism.

For example, rather than having a monolithic update loop that goes over every mob and updates each of them in turn, it might be better to have a parallel loop that concurrently builds a list of mobs that require updating, and then monolithically update those that need updating in turn.

Further, it might be possible to get a little task parallelism, and concurrently update those mobs. For example, by holding a coarse grained lock such as a lock on the area the mob is currently in. Beware that you will need to either establish a locking priority on the areas (such as alphabetically, or by vnum), or build a second list to monolithically run through those updates that require access to multiple areas.

2) user I/O can be effectively task-parallelised. User A wont care what you're writing out to User B's socket. Nor what you are reading. Rudimentary parsing can be performed (such as looking for an end-of-line marker), before queueing up the actions to take place in the game world's message queue.

I would suggest that, at a maximum, you have a pool of 2N threads, from which all work is done, where N is the number of hardware threads you have (e.g. quad-core with hyperthreading, N=8). You wont be able to do more work than N anyway, but a number of threads will be I/O bound, so they wont be interfering that much.

(I'm gonna postfix a standard warning about profiling and measuring, however)

A caveat: I am looking at this from the perspective of an application that works in character, rather than line mode, and without local echo. So it would not do for the game to lag a user's gui just because we're doing some pathfinding for someone else. Your situation may differ.
28 Jul, 2011, Rarva.Riendf wrote in the 6th comment:
Votes: 0
Kaz said:
Further, it might be possible to get a little task parallelism, and concurrently update those mobs. For example, by holding a coarse grained lock such as a lock on the area the mob is currently in. Beware that you will need to either establish a locking priority on the areas (such as alphabetically, or by vnum), or build a second list to monolithically run through those updates that require access to multiple areas.

Locks are really expensive (and always hard to identify, so dangerous). It is better to identify wich operation can be safely done without interaction on others. Like object decaying as an example. You can just cut your list in as may processor you have (actually a lot more than that), and have each of them run the same code decaying items (provided the decaying only extract the object, it is just an example)
Only go with more complicated system if you really need the performance. Multithreading code can be very misleading. Sometimes it is better to process more data than necessary than to use a lock to avoid it. ( I have made the experience first hand, I made a system that was parsing data, calculating something and removing it from a list. With possible duplicate in the data. It was better to to calculate all the datas, even with lot of duplicates (so 'useless') (with multiple thread) than to check if I had already calculated the solution (because for that i had to actually store someyhing in a list (so have a lock mecanism on it ) the data I already calcultated)

Quote
I would suggest that, at a maximum, you have a pool of 2N threads, from which all work is done, where N is the number of hardware threads you have (e.g. quad-core with hyperthreading, N=8). You wont be able to do more work than N anyway, but a number of threads will be I/O bound, so they wont be interfering that much.

That does not really work like that. I have a quadcore, and my program was way more efficient with 55 threads launched (but more than that did nothing, and it was starting to decrease performance with about 100) than just 8 or even 16, and noticeably.
It really all depends on what you do (for file IO the OS is doing it for you anyway (provided you a NCQ enable hard drive), so just queue all of them asynchronously)
28 Jul, 2011, Runter wrote in the 7th comment:
Votes: 0
Good multithreaded design has the benefit of firing on all cylinders at the expense of maintainability (usually) and efficiency. For example, let's say you had an application that's capped out on a single core and you want to utilize a second. Even if you utilize the full second core, it will not safely speed up your application x2. You could, as an example, end up using 100% of both cores while only having your application doing 50% more useful work. The numbers get worse when you're trying to perfectly distribute work across more cores. That's not to say it's not better (and in some architecture required.). But those wanting to design an application to be multithreaded need to consider the real problem of actual multithreaded design vs just using threads for concurrency. It's easy to use threads for concurrency and deferments without going full blown multithreaded design. And the gains there can be surprisingly good.
29 Jul, 2011, Mastermosley wrote in the 8th comment:
Votes: 0
Thanks for all the information guys, really appreciate it.
01 Aug, 2011, David Haley wrote in the 9th comment:
Votes: 0
There was a big thread here about multithreading maybe a couple of years ago. The short answer is that it's not really worth it – none of the use cases presented here make the case convincingly, at least.
01 Aug, 2011, Rarva.Riendf wrote in the 10th comment:
Votes: 0
David Haley said:
There was a big thread here about multithreading maybe a couple of years ago. The short answer is that it's not really worth it – none of the use cases presented here make the case convincingly, at least.

Fully agree. First step is always to try solve your problem (considering you have one, that is not even sure here) without multithreading. As multithreading really opens a can of worms.
0.0/10