Threads in BlitzMax
Miscellaneous Forums/General Discussion/Threads in BlitzMax
PureBASIC has threads for multi-tasking time consuming events in the background - what about BlitzMAX? It doesn't have them as far as I'm aware.
This seems like a priority for loading levels for example while gameplay continues - like Grand Theft Auto (rather than stopping the whole game to load).
Maybe someone will say otherwise, (this is just from memory)
But, the assembler used to create the Bmax exe is not thread safe. So only one thread is recomended.
Someone, and Id guess Dream, posted that the update of the compiler is thread safe, but that this new compiler isnt stable on Macs
Even if they just let us have ONE extra thread that would be very useful.
To implement threading, of sorts, you could look at using a scripting system that has the ability to multitask several scripts at once from within a single app. That's what I'm doing at the moment. The only difference is each thread has to run on the same one CPU, it's not multiprocessing like real threads can be.
BlitzMax for multiprocessing would be cool, for all these dual-tri-quad core things coming out. Ie run one thread per core. Surely that would be safe ish?
The problem is that the Garbage Collector isn't thread-safe. Additionally BlitzMAX does not provide a mechanism for preventing concurrent access to critical sectors.
>This seems like a priority for loading levels for example
>while gameplay continues - like Grand Theft Auto (rather
>than stopping the whole game to load).
emulate threading until BMX get's there.
Andy
After Max3D is released, there will be no excuse for BRL to avoid making Max thread safe... if they want to release a next gen 3D engine, they need to ensure the language that uses that engine is capable of next gen activities too. Multithreading is an obvious necessity for really stupidly large games.
Threading would be really nice.
With the new DLL support, you could probably successfully thread BMAX. As each Garbage collector of the DLL is seperate from the the Garbage collector in BMAX executable.
It my require a few stratigic criticalsections to access memory or objects fields but should be doable.
I guess I need to play with the DLL support and do a proof of concept.
Any interest in a network layer that runs in a thread?
Has anyone built a .so in Linux or Mac?
Doug Stastny
You'll still need to make sure one GCs don't inadvertently collect objects that the other is using.
The two GCs cant see each other than have different heaps. The area I am little unsure of is how the stack is scanned but I believe, need to read the code more but think thats what GCEnter GCLeave are used to manage.
Doug Stastny
But they'll both have to be able to see the critical sector, won't they?
No only the one in the DLL.
Since any exported function in the DLL might need to grab the critical section. I say might as it all depends on how objects are interfaced with.
This is no different than any other language.
For example this simle Max function does not interact with the the Garbage collector in any way. And in no case will GCCollect be called.
Call it once call it a million times GCCollect will never be invoked. Just look at the ASM output of the BMax Compiler.
function Add:Int(X:Int,Y:Int)
return X+Y
end function
This function might invoke the Garbage Collector. In max only calling new will call a collect, even then it will only do it every 500 times you call new.
function CreateObject:Int()
Return HandleFromObject(New Object)
end Function
This really should be solvable. A function to create the objects is where the Critical Section in the DLL would need to be. I just need to get handle on GCEnter, GCLeave as those are the functions that make this key as they play with the Stack Frame of the Garbage Collector.
function NewObject:TObject()
local o : TObject
<EnterCriticalSEction>
GCEnter()
o := new Object
GCLeave()
<ExitCriticalSection>
end Function
Need to find some time to work out a demo.
Doug Stastny
Multithreading is an obvious necessity for really stupidly large games.
And obvious, simple things, like music and networking, too.
Having possibility to activate thread would be very good but it may slow bmax too because it must check each mod too , well depend of what you are doing. It why an option to activate the command is something to consider I think.
There is a threading module out there, although it is very tricky to get to work without crashing, but it does work.
http://blitzbasic.com/Community/posts.php?topic=54991#613357 Link is in there
Official multithreading would be great!
It doesn't work. It has concurancy issues aswell.
With the rising popularity of dual-core processors and the quad core and higher being just around the corner as well, sooner or later there really needs to be a threading module, or BMax won't be able to keep up its competitive edge...
Mark has indicated in the past that he had been thinking about the whole threading thing, but the 3D module is a higher priority at this time.
But one can hope that at least some form of threading will appear in the future.
Uuuhh, yes i do also some JAVA-Programming.
So i adorable like Threads. ;)
Threading in BlitzMAX would be a kick-ass-feature.
(don't forget the Linux- and MAC-user)
yeah animated loading screens without manually re-writing all the Blitz loading routines to do so a byte at a time would be nice.
and a big mac with fries while you're at it too.
Grey - like this?:
See code archive link below.
Sorry guys, no time for making Max thread safe. That ARM processor needs supporting in the most desperate way!
Grey - like this?:
The problem with that is, that while it works - it is at least an order of magnitude slower than doing it in multiple threads. Why? Because every time you load something, your thread forces a context switch while your application changes state from running to waiting for I/O. This gives a very low degree of time-slice utilization. Sure it works, but doing it in seperate threads means that:
1) Your main thread gets the full use of its time-slice because it does not context switch until it a) voluntarilly turns control over to the OS becuase it's finished doing what its doing or b) is pre-empted.
2) Your loading thread gets the full use of its time-slice aswell because it doesn't have to force a context switch from I/O to running states. Additionally since it can stay in the I/O state for longer (the full extent of its time-slice), it can buffer larger amounts of data in one go, thus improving I/O performance.
Beaker, wow yeah that's pretty cool. I've done byte loaders in Delphi but not BMax before and I wasn't sure how to get the transferred data into an image. No if that code could be done in c++ and part of BMax, that might make it a bit faster? It's good you can tweak the nibble size.
I've edited my code above to show a more realistic 'nibble' size (as mentioned by Grey). With settings of between 10 and 20 I don't really see speed being an issue, and the [lack of] buffering mentioned by FlameDuck is surely taken care of?
I take Flames general points tho, which are probably more relevant to other uses for threads.
Updated version of above code
here.Now supports remote files (HTTP).
nice code beak... but doing it in threads is the best way.
Beaker: Very nice.
Of course, depending on how how accurate you want your progress bar and how large the file is, Beakers code is sped up *tremendously* by picking a larger nibble size.
yeah very nice, now to find the time to use it properly!
Sorry guys, no time for making Max thread safe. That ARM processor needs supporting in the most desperate way!
Heh... My suggestion to forget compiling to CPU's and concentrate on intermediate language layers such as the JVM and CLR were glossed over in that thread.
Ironically, implementing threading the safest and most straightforward way (even with built-in closure) would be relatively trivial if Max were creating CLR and JVM syntax trees rather than native code.
<quote>
Ironically, implementing threading the safest and most straightforward way (even with built-in closure) would be relatively trivial if Max were creating CLR and JVM syntax trees rather than native code.
</qoute>
But then the performance would suck. CLR and JVM are good for high end hardware and business apps. But for math intensive and memory intensive applications like games they suck.
Although I agree, I wish BLR would spend more time on the language. Threads would be nice, just the language elements should be thread safe the GUI and Max2d dont need it right away. Although I am unsure how many of you realize this if BMAX was thread safe it would be overall slower. Now that can be coded around but there is already alot of code, so thread safety would have to be a switch.
Its the nature of threads. All memory allocations and Deallocations need to be inside critical section and that is expenisve OS/CPU operation.
I am still working on trying to figure out if I can make a thread work well from DLL keeping the memory managers seperate.
Doug Stastny
Doug Stastny
Heh... My suggestion to forget compiling to CPU's and concentrate on intermediate language layers such as the JVM and CLR were glossed over in that thread.
Any comments that weren't phrased as "YES YES YES!" were glossed over. Sad really.
I'm of the opinion that threads added to BlitzMax would put it beyond the competition by a mile as far as games are concerned, and would add significant power. The 3d module is not as important.
Mark, please take some time out to think on their introduction before another layer is introduced (the 3d module).
All memory allocations and Deallocations need to be inside critical section and that is expenisve OS/CPU operation.
No they don't. Only resources that are shared between threads need to be inside critical sections.
@FlameDuck yes they do.
Have you ever written a multithreaded Memory manager? If memory from the heap is shared between threads the alloations and deallocations need to be inside a critical section. Why do you think you cant thread max?
Take a look at the source and get back to me. But your incorrect.
Doug Stastny
But then the performance would suck. CLR and JVM are good for high end hardware and business apps. But for math intensive and memory intensive applications like games they suck.
Hmmm... I wonder if Microsoft is aware of this considering the XNA platform is based entirely on managed code. Sun has a dedicated team and java community for game development.
Bad performance of CLR and JVM for high speed applications like game software was certainly the case in the recent past (like as recent 1 or 2 years ago). Not so much anymore.
Although I agree, I wish BLR would spend more time on the language. Threads would be nice, just the language elements should be thread safe the GUI and Max2d dont need it right away. Although I am unsure how many of you realize this if BMAX was thread safe it would be overall slower. Now that can be coded around but there is already alot of code, so thread safety would have to be a switch.
That would differ from machine to machine, and would depend heavily on the amount and types of cache present on the CPU(s).
To support your point though, the types of cache on CPU's of the average gamer's PC are not designed for a lot of multi-threaded operations. Cache thrashing would be a bit more prevalent, performance would indeed suffer, and it would probably be less performant compared to the same application running on a single thread.
Ok, here's a quick summary of the 'thread' situation.
Note that much of this may or may not be correct. I *have* thought long and hard about mutlithreading, but it's a complex issue and much of the below is based on how I *asssume* things work in other systems...
The biggest obstacle to implementing threads in BlitzMax is the reference counting garbage collection system.
The problem is that every reference count increment/decrement needs to be atomic, and the overhead of this is significant - it would slow down assignments to non-local variables by a factor of around 10. In ASM, it only involves adding a 'lock' prefix to the inc/dec (see the bottom of blitz_gc.h), but it's expensive.
Reference count increments/decrements are what is known is GC-speak as a 'write barrier' - ie: they are little bits of code that gets executed whenever a 'reference' is written. In coding terms, this means a variable object assignment.
Most GC systems involve a write barrier - and some, like BlitzMax's, require atomicity. However, some other GC's, like .NETs, do not.
.NET uses a 'generational' GC, which uses a write barrier that simply sets a bit in a look up table. This doesn't need to be atomic, because it doesn't matter if 2 threads set the same bit at the same time - synchronizing the threads wouldn't change the result.
As for Java, I believe there are several GC's in existance for Java now - some probably require atomic write barriers, some probably don't.
So, why doesn't BlitzMax use a generational GC? Well, the main reason is because reference counting seemed to me to be the most sensible at the time I was creating BlitzMax - But I wasn't really thinking about ,multithreading at that time...
It should also be noted that generational GC's are apparantly not good at handling objects with a 'medium' lifespan - I have no idea what this means exactly, but it does sound a bit like yer typical game object - ie: it's not here for an entire level, but it'll be be around longer than one mainloop!
Yet, it may be that BlitzMax - even with it's requirement for atomic reference count incs/decs - would be faster than .NET, because BlitzMax uses a pretty cool trick that removes the need for a write barrier when assigning to 'Local' variables.
This of course accounts for many, many assignements, and in a way it changes the question to 'does the average program use more than 10x the amount of assignments to local variables than non-local variables'?
If the answer is yes, then BlitzMax - even with atomic incs/decs, ie: a multithread BlitzMax - would theoretically perform faster than .NET GC-wise.
BUT! It would still perform much slower than current BlitzMax, so it gives cause for caution...
Since CPU clock speeds appear to have hit a wall, it would appear that multiple cores are to be the saviour of 'faster computing' - which, as game developers, is most certainly our field of interest.
Yet, beyond simple "animating a title screen while the graphics are loading" scenarios, it is extremely difficult to multithread a game as games tend to be extremely sequential - you can't RenderWorld until you UpdateWorld, as you need to know where everything is. If you're willing to swallow a frame's worth of lag, then yes, you can RenderWorld and UpdateWorld simultaneously - but I'm not and it's not really a complete solution.
In that sense, I'd hate to add the ability to 'animate title screens while a game is loading' at the cost of hurting the execution speed of the actual, single-threaded-by-necessity, game.
OS's don't help much either - there is a very sexy, simple way to synchronize multiple threads using thread priorities. Yey neither Windows, Linux, or MacOS reliably support this method. It is apparently a feature restricted to 'realtime' OSs like QNX, but I have no idea why.
I do believe that as CPU's develop more and more cores, OS's need to come up with ways to allow us to exploit them to the max!
In a way, I'm surprised CPU designer's haven't learned more from the GPU dudes - GPU shaders depend upon an awesomely elegant, although admittedly limited, system to be able to stick as much stuff in parallel as possible, with no programmer intervention at all - ie: you don't have to know how many 'shader pipelines' a GPU has - it just works!
The CPU equivalent to what GPU's do may be a special 'foreach' instruction built-in to the CPU. This would allow you to iterate though, say, arrays, executing the same chunk of code for each element, in parallel.
Perhaps this is what the PS3 cell stuff is all about? Ha, good one! and the reason it's so arse-achingly expensive is because they're gonna actually sell them for a profit and let 'common people' like us develp for it without having to pay a royalty!
Anyway, the bottom line is multithreading is tricky, and while it's possible, it may not really be worth it.
Well, thanks for the very-well-explained answer. I like when people explains what can be done and what can not be done and why.
If memory from the heap is shared between threads the alloations and deallocations need to be inside a critical section.
Have more than one heap. Like say one heap per monitor.
Good read that mark! :)
Though, I could only understand half of it! ;)
Dabz
@Flameduck that is possible, but consider a mulitthreaded TCIP server. You have one thread processing messages and allocating them on the heap per connection. You still need to be able to access and manage those most likely on a different thread. There in lies the problem.
@RocketGnome I am aware that there is platforms written to allow game writing in managed lanuges CLR, JVM. However look at the games. Seen any trible AAA titles? Can you write Pacman, or a puzzle game sure. Never discounted that.
The key for this type of question is whats the target machine.
@Mark nice explaination of your GC. The big problem with not having any thread support is not so much for loader screens as its for dealing with Network game servers from my perspective. If the main thread stalls processing UI or rendering, you still need to maintain network connections.
If you follow up on thread a couple questions.
Since if we compile BMax into a DLL it gets a serperate Garbage Collector it should be theoeritcally possible to have seperate threads in seperate DLLs. I am trying to work through this but little explaintation of GCEnter, GCLeave might help. I see it GCEnter sets the Stack Pointer for where you will start scanning the stack for local "alive objects" variables. So if I was to write a threaded dll i would assume that I would I changed context owner of the GC I would need to call GCCollect then GCLeave before entering a new stack frame with GCEnter.
Doug Stastny
While I'm not as technically saavy as to question mark... I will anyway. :)
Multiple threads would be much more important than just animating a title screen and loading things in the background. As budman says, networking in it's own thread is one application of it. And while you have to updateworld before you renderworld... You could have a half dozen threads out there doing the update with a "management" thread that initiates the other threads, and deetermines when it's possible to renderworld.
Right now, with your mention of single cpu core chips still the norm instead of the exception, it makes less sense to handle it like that. But as dual (and next year, quad) core chips become more prevalent in the market, and the core speeds fall (my dual core runs slower per core than my last single core cpu did) then tossing out threads for different cpu's to handle makes more and more sense.
My take? Threads in bmax right now... a luxury we can do without. As the multi-core processors start entering the market in larger numbers... a necessity we will require.
Purebasic has threading btw... And I *think* powerbasic as well. Neither are all that hot for game writing, but they do implement it.
Budman - have you tried
Etna for handling your networking? It is threaded.
Also, good luck with your threaded DLL code.
@Beaker - thank, I actually have coded ENet in C++ application using threads. My usage of BMAX is pretty much for fun to see what I can make it do. Professionaly I program C/C++ and Delphi. Been doing quite a bit with C# lately.
The reason I like Max is native code, no JVM, or CLR and ability to cross compile the same source. Although I did write the Max2d for DX9. Its so close to being a great language, but it still lacking.
Thanks for encouragement.
Doug Stastny
Yet, beyond simple "animating a title screen while the graphics are loading" scenarios, it is extremely difficult to multithread a game as games tend to be extremely sequential - you can't RenderWorld until you UpdateWorld, as you need to know where everything is. If you're willing to swallow a frame's worth of lag, then yes, you can RenderWorld and UpdateWorld simultaneously - but I'm not and it's not really a complete solution.
But there are other things other than an 'animated loading screen' that would greatly benefit from the threading ability:
- TCP/IP networking -- especially since a slow server response can grind everything to a halt
- Pathfinding
- Enemy AI
- 3rd party sound libraries
And I'm sure there are more.
Unfortunately(?) it looks like pretty much all CPU enhancements in the near future are based around adding more and more cores, rather than rasing the speed of the individual cores.
I am aware that there is platforms written to allow game writing in managed lanuges CLR, JVM. However look at the games. Seen any trible AAA titles? Can you write Pacman, or a puzzle game sure. Never discounted that.
Point taken. However, BlitzMax has also yet to produce something AAA. For 95% of the cases out there, you wouldn't be able to tell the difference other than the game crashes less frequently or harder, and took substantially less time to develop when running under managed code.
N-Gen vs. CLR/JVM is mostly a religious debate that really disguises itself as an engineering vs. architecting standpoint. Personally, I believe the future is intermediate language compilers. This type of abstraction brings us the closest we're going to get to one unified platform.
If the main thread stalls processing UI or rendering, you still need to maintain network connections.
Good example. Bad network code can literally take over your computer if it gets hung up in your main thread.
Unfortunately(?) it looks like pretty much all CPU enhancements in the near future are based around adding more and more cores, rather than rasing the speed of the individual cores.
Yes. The learning curve for developers is going to jump quite a bit. Of course, there will likely be some abstraction placed around a good amount of the technical bits. In the future, a lot of applications will need to be written with a mind for multiple CPUs/Cores/Threads.