Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
Due to spam on this forum, all posts now need moderator approval.
David Haley said: It is perhaps not your fault in this instance because you have little choice, but, well, your disagreement doesn't change the fact that it's what you're doing that's messing things up, not some inherent slowness in MUSHclient.
As far as I know, the inability to do work outside of the GUI thread is inherent to the design of the program. So forgive me for thinking that the buck stops there. I'd love to see counterexamples.
Quote: Would it be reasonable for somebody to complain to Nick about MUSHclient being slow because they're doing a full mini-max search routine to optimize some AI agent's playing strength in a plugin?
If this search routine is so popular that it spawns dozens of threads from people saying "Hey, that's a neat idea. I'd like to adapt it for my MUD too!" like the graphical bigmap and the gmcp mapper, and if hundreds of people want to use it but find that it's too slow as it is, then absolutely 100%. That's how the program advances. I'm concerned that you think otherwise.
One of the problems I have with this talk of MUSHclient being "extremely slow" and then, after certain changes are made "much faster" is that it is so vague as to be almost meaningless.
Before you can improve a metric you need to measure it. In my case in an earlier thread about the mapper speed I actually timed running from A to B (which turned out to take around 18 seconds to run 60 rooms, which I would not personally call "extremely slow"). Then I made changes to the search depth and got the speed down to 9 seconds for the same run.
I really think to get anywhere you need to post figures and give environments. For example:
On my 2.5 GHz Pentium with 2 Gb RAM
I have MUSHclient's window set to 2500 x 1500 pixels
I have 32-bit colour depth on my monitor
My search depth in the mapper is set to 10 rooms
I have these plugins installed: A, B, C, D
I have these options enabled which might cause a lot of comms output (eg. the ASCII room map)
My map window is sized to 200 x 300 pixels
I started at "recall"
I ran to "weather station" which is 60 rooms away.
It took 14 seconds on my stopwatch
Then, adding the following optimizations (listed) to my plugin the travel time reduced to 8 seconds.
This is stuff you can measure. Words are vague. You can say 9 seconds is too slow, others might say it is amazingly fast.
I already pointed out that if your goal is to move 60 rooms in 5 seconds, then drawing a large search depth is ridiculous - how can you possibly absorb thousands of rooms appearing on the screen in 5 seconds?
Also you need to take into account absolute and relative values. For example, you might say reducing the speed from 2 seconds to 1 second "cut the time in half". I might say "well, you only saved a second".
Another thing you can look at, if you want to keep the mapper going while you move rapidly, is to simply rewrite the mapper module in C++. By removing the interpreted language doing the CPU-intensive search-and-draw, you might actually reduce the time taken by the mapper by quite a lot.
As far as I know, the inability to do work outside of the GUI thread is inherent to the design of the program. So forgive me for thinking that the buck stops there. I'd love to see counterexamples.
What could work quite well in your case, and adapt itself to drawing lots of rooms if you are standing still, and not as many if you are moving, is coroutines.
The inner thread of the mapper (which does the search) could be a coroutine, yielding at each change of search depth. So it draws 1 room out (eg. 4 rooms) and yields. Then 2 rooms out, and yields again, etc. I think that is a natural part of the mapper loop anyway.
Then you need to resume of course, and this is where the OnPluginTick could be used. If nothing much is happening, it fires frequently, and you can keep drawing the map (by resuming the coroutine).
If you have moved rooms, then the mapper basically stops drawing this room and starts again. I'm not sure how responsive this would be, you may need to make it yield every 3 search depths for example. Or maybe a single yield after drawing two rooms out would work well for fast moving, and then if it gets control back (for that room) you draw the rest. Or some combination.
Fiendish said:
I think you're barking up the wrong tree here. The amount of data streaming through is pretty negligible. You're talking about the order of hundreds of bytes per second, not a whole lot. You can even disable the automap and the speed will still be just as slow, despite now sending less than half the lines.
Did you actually measure this? My point was that hundreds of bytes or not, this is causing Windows events that delay the timer. Each line, for example, might (or might not be) a new event.
The other thing to take into account here is future maintainability. To shave off maybe a couple of seconds, you might have made the code so convoluted that no-one else can ever maintain it. Maybe.
I enabled timing and tried the test again (on the earlier version of the mapper, without your Redraw improvements). On one room for example:
Time to draw 1114 rooms = 0.486 seconds, search depth = 30
OK, over 1,000 rooms drawn in half a second. Not too bad, ramping back the depth would have helped.
Total for the runto weather station (search depth 30):
Cumulative: 71342 rooms in 32.823 seconds
That's drawing 2,173 rooms a second. Sure, 4,000 rooms a second would be faster. But hardly "extremely slow". And yes, this is slower than my earlier test. I think something is wrong with my Mac. Maybe the new RAM is behaving strangely.
But again I think you need to measure things. Rather than saying "slow" or "extremely slow" or "too slow" we could say something like: "I measured a draw rate of 2,000 rooms per second in the mapper. I would like a rate of 3,000 rooms per second". Now we have a goal we can aim for and measure.
So in this example, I dropped the search depth down to 8 and tried again:
Cumulative: 7159 rooms in 2.480 seconds
This time, 2,887 rooms per second.
Interestingly, dropping down further to a search depth of 5:
Cumulative: 2650 rooms in 1.129 seconds
That is 2,347 rooms per second. So actually worse than a search depth of 8.
So this shows that blindly following one path may not necessarily give the best results. But I do think that trying one thing, and carefully measuring, and finding the "sweet spot" is a helpful way of improving performance.
Fiendish, I'm not sure what you're trying to say. My point is straightforward: doing computationally intensive tasks in the GUI thread is a bad idea, plain and simple. This is a well-known issue with GUI programming. I already said that in this instance, you have little choice in the matter given how the framework is set up. I also gave some options that Nick could implement to work around this. I also gave an option that you could try: coroutines. What is it that you want, exactly? It seems that you expect there to be some solution that will fix things without creating work. You've been using somewhat vague metrics when discussing slowness which leads me to believe that you haven't actually measured this and do not know if the slowness is your fault, Nick's fault, or due to doing computationally intensive work in the GUI thread.
Even if Nick does adopt one of the solutions I mentioned, your code will have to be changed, perhaps quite a lot, to be made multithreading-aware.
David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone
Just to clarify the posts above, the rooms counts are "room boxes" drawn on the screen. The actual "MUD rooms" travelled was 73 rooms (from recall to Weather Station). My latest test which counted those showed:
Cumulative: 9776 rooms in 2.579 seconds (3790 per second)
We have traversed 73 rooms (28 per second)
So in this case the mapper actually drew 28 different rooms per second. So in effect this is 28 FPS in movie terminology (where modern movies are 24 FPS, although NTSC video screens at 30 FPS and PAL at 25 FPS).
I think it's pretty clear that MUSHclient's rendering etc. are not responsible for the slowness. It doesn't take a lot of processing in plugins that interrupt the GUI drawing for a program to appear very slow and unresponsive.
David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone
Amended on Fri 03 Dec 2010 12:57 AM (UTC) by Nick Gammon
Message
I tried out the coroutine idea. Basically the "draw" function was renamed "draw_thread" and then these lines added:
function draw (uid)
thread = coroutine.create (draw_thread)
assert (coroutine.resume (thread, uid))
end -- draw
function resume_drawing ()
if thread then
assert (coroutine.resume (thread))
Redraw ()
end -- if
end -- resume_drawing
Inside draw_thread I yielded for each search loop:
while #rooms_to_be_drawn > 0 and depth < config.SCAN.depth do
local old_generation = rooms_to_be_drawn
rooms_to_be_drawn = {} -- new generation
for i, part in ipairs (old_generation) do
draw_room (part.uid, part.path, part.x, part.y)
end -- for each existing room
depth = depth + 1
coroutine.yield ()
end -- while all rooms_to_be_drawn
At the end of draw_thread I did this:
thread = nil -- indicate finished
That is so you don't resume a dead coroutine.
Finally inside the mapper plugin (not the mapper.lua) I added this to push the drawing along:
function OnPluginTick ()
mapper.resume_drawing ()
end -- OnPluginTick
I also moved around the drawing loop (above, with the yield in it) to lower down, otherwise you don't see the map, border etc. at all until it is finished.
The end result? Well I laughed when I saw it. The rooms now draw in a sort of "starburst" way, starting at the middle and gracefully filling outwards in all directions (which is what I expected).
It's a bit slow to draw, given that it waits 40 milliseconds (the OnPluginTick inverval) for each extra layer. That could probably be fixed by making it draw four layers or so rather than one, at a time.
However what does happen is that when running around, it only draws the current room, thus significantly speeding up the time taken when on the move.
In fact, this seems to compromise quite well:
if depth % 4 == 0 then
coroutine.yield ()
end -- if
So it draws 4 levels out, then yields. This makes drawing pretty fast if you are just looking around, and only draws 4 levels if you are speedwalking.
Out of curiosity, is Fiendish's slowness report regarding rendering, or regarding the search routine that his plugins are running? Or just in general?
My impression is he is talking about the overall experience. It is a combination of CPU being used by drawing the map, which then delays the time it can process incoming text from the MUD, which then means screen updates are deferred (being lower priority) and timers are deferred (being lower priority) so it has a knock-on effect.
From the video posted above I personally wouldn't say it is "extremely slow".
Amended on Fri 03 Dec 2010 12:01 AM (UTC) by Twisol
Message
That's a really cool optimization. I actually very much like how it looks when you're walking around quickly. My only qualm is that the golden rings (which I assume are "special points" of some kind) only appear when everything is drawn, even though the layer they're on may have been drawn for some time.
[EDIT]: I understand that the rings overlap nearby rooms, so if you did do it as the layer is drawn, the rings might be drawn underneath the neighboring rooms. However, if you used a separate transparent layer for those points and simply merged the layers together right before updating the map, that might work better.
The rings are area transitions (area A to area B). Until you draw area B you don't know the ring will be needed. I suppose the rings could be cumulatively drawn, but really the time when you are running around and the map is half-drawn you probably don't care, until you stop and evaluate where you are.
In any case, the thing is a proof of concept, not a finished product. For another thing, the rooms down the bottom overwrite the area name. Some minor tweaking would be needed before this amended version went live.
It does however show the possibility of doing a map that gobbles up CPU selectively - if it is available you see lots of rooms, if things are pretty busy, it does less.
The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).
To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.