PDA

View Full Version : Multithread in Maxscript


Panayot
11-27-2011, 07:43 AM
Not once I seen peoples argue that we have multithreads in Maxscript.
Of course we can run .net threads (that execute scripts) in Max but
how does this make Maxscript truly multithread?

I'll be happy if someone prove this somehow with sample code.
Thanks

lo
11-27-2011, 09:49 AM
well, what is your definition of 'truly multithreaded'? What kind of proof do you expect to see?

jonadb
11-27-2011, 10:02 AM
Here some sample code I did while doing a mini challenge a while back:

http://forums.cgsociety.org/showpost.php?p=6986723&postcount=79

And remember, multi thread != multicore. How threads are distributed over the cores is something the OS takes care of.

Panayot
11-27-2011, 11:34 AM
Thanks to both.

Rotem, good question :) multithreads are not very clean subject to me, so it is possible some lack of understanding from my side, maybe Jonathan's example answer already, s'd check, but here is my definition.

Lets say i have 2 blocks of codes taskA() and taskB(), each one do different job and each one if runs alone will finish it work for 1 second. But if those tasks run at 2 threads they s'd finish for 1 or closer to 1 sec, and for sure not for 2 seconds, right?

Also lets say taskA update some data, global variables for instance, and that happens at time 0.1 sec after it start, when taskB s'd can get fresh data at that time or very soon after that, i.e. not just at the end of taskA (after 1 sec.).

In other words, i suspect that the code blocks executed sequential, and that is not 'truly multithreaded', but of'cuz i might be wrong, and this is what i want to clarify to myself.

Thanks in advance!

lo
11-27-2011, 11:47 AM
The code blocks are not executed sequentially, they are indeed executed in parallel.
The degree to which code is executed faster as more threads are used is known as the code's scalability, and is very dependent on the code being executed.
If a block of code executed in X seconds on one thread and in X/N seconds on N threads, then it is said that the code scales linearly, which is the best possible situation.
The ideal number of threads to use for a particular task depends heavily on the code and on the hardware. Most likely using 100 threads for a certain task will be slower than using 10 threads due to the overhead of starting and managing that many threads.

As for code blocks accessing the same data together, it is up to you to ensure your code is 'thread-safe', and does not cause fatal collisions. There was a thread on this topic not long ago.
Maxscript does not currently contain any instruments to help you with this task, as it
was not designed for multithreaded execution, but as far as I remember, the main guideline was that you must not redraw the viewport from a background thread.

Panayot
11-27-2011, 01:06 PM
The code blocks are not executed sequentially, they are indeed executed in parallel.
The degree to which code is executed faster as more threads are used is known as the code's scalability, and is very dependent on the code being executed.
If a block of code executed in X seconds on one thread and in X/N seconds on N threads, then it is said that the code scales linearly, which is the best possible situation.
The ideal number of threads to use for a particular task depends heavily on the code and on the hardware. Most likely using 100 threads for a certain task will be slower than using 10 threads due to the overhead of starting and managing that many threads.

As for code blocks accessing the same data together, it is up to you to ensure your code is 'thread-safe', and does not cause fatal collisions. There was a thread on this topic not long ago.
Maxscript does not currently contain any instruments to help you with this task, as it
was not designed for multithreaded execution, but as far as I remember, the main guideline was that you must not redraw the viewport from a background thread.
Excellent! That was all what I need to know, many thanks!

MikieK
11-27-2011, 07:29 PM
FYI at time <n> does not work in multiple threads...

TheGrak
11-28-2011, 05:22 PM
but as far as I remember, the main guideline was that you must not redraw the viewport from a background thread.
^ That helps make a background worker thread more stable in some cases, but it can still crash max if the user is doing something when the thread tries to update the viewport when it's finished (like merging a max file in the background, or creating a box, updating a mesh, etc...). For doing calculations and non-viewport related stuff, the background worker is great. I wish it worked 100% of the time.

I did some race condition tests on the background worker and found that I could not create a fatal collision between two functions operating on the same mesh at the same time. This has led me to believe that a .net background thread does not execute at the same time as the maxscript "main" thread in 3ds max. In addition to that assumption, I think that the main thread has precedence over any background threads, meaning that identical functions will take different times to compute depending on if they are in the main thread or a background thread.

For example, here are two identical functions operating on the same array, yet the background function finishes after the foreground function, despite being called first:


Race 2:
( --which thread finishes last?
local myBox = box() --create a box()
--create an array, fill the array with references to the box
local myArray = #(myBox, myBox, myBox, myBox, myBox, myBox, myBox, myBox, myBox, myBox)

fn bkgWrkr =
( --move the box position in a background thread
for i=1 to myArray.count do
(myArray[i].pos = [0,100,0])
)
fn foregroundWrkr =
( --move the box position in a foreground thread
for i=1 to myArray.count do
(myArray[i].pos = [0,0,0])
)

MainThread = dotnetobject "CSharpUtilities.SynchronizingBackgroundWorker"
dotNet.addEventHandler MainThread "DoWork" bkgWrkr
MainThread.RunWorkerAsync()
foregroundWrkr()
) --in this example, the bkgWrkr always finishes last (for me)


I think any function put into a background thread takes a slight performance hit due to the precedence of the main thread, if any code is executing in the main thread. So, if you care about performance and stability, you probably shouldn't put your code in a background thread. And I love the background thread too - I'm not hatin' - just being honest about my discoveries. :)

lo
11-28-2011, 05:52 PM
I have a few remarks about this race test:

* It is somewhat unfair due to the extremely fast task you are measuring. If you benchmark this task it takes less than 1 milisecond for the foreground worker to complete, so by the time the background worker sets up a thread to work with, the foreground worker is done.

* Consider this test instead:
( --which thread finishes last?
local myBox = box() --create a box()
--create an array, fill the array with references to the box
local myArray = for i = 1 to 200 collect myBox
global bgStart = undefined, fgStart = undefined, bgEnd = undefined, fgEnd = undefined

fn bkgWrkr =
(
::bgStart = timestamp()
for i=1 to myArray.count do
(myArray[i].pos = [0,100,0])
::bgEnd = timestamp()
)
fn foregroundWrkr =
(
::fgStart = timestamp()
for i=1 to myArray.count do
(myArray[i].pos = [0,0,0])
::fgEnd = timestamp()
)

MainThread = dotnetobject "CSharpUtilities.SynchronizingBackgroundWorker"
dotNet.addEventHandler MainThread "DoWork" bkgWrkr
MainThread.RunWorkerAsync()
foregroundWrkr()
sleep 2 --let everyone catch up
format "BG thread started % ms after FG thread and finished % ms after it\n" (bgStart-fgStart) (bgEnd-fgEnd)
)

half the times you will get a result proving that the BG worker started working after the FG worker, and finished its work by the same offset.
Other times, max will crash due to the race condition.

lo
11-28-2011, 05:59 PM
So, if you care about performance and stability, you probably shouldn't put your code in a background thread.

I think that is a somewhat misleading statement.
If your task can benefit from parallel execution, you should probably put it in a background thread, ideally more than one. That is the whole idea of multithreading.
If your task does not benefit from parallel execution, or due to thread conflicts is not stable when executed in parallel, you shouldn't put your code in a background thread.

TheGrak
12-02-2011, 10:21 AM
I think that is a somewhat misleading statement.
I agree with you now, looking back on that statement.

Kickflipkid687
01-06-2012, 09:28 PM
I'm probably going to look into this tomorrow for a bit and see what I can find.

I'm trying to not use the SDK, just normal script, but instance/copy objects as fast as possible. Right now I can do around 1100 a second if all goes well. However i'd like to try and do this faster if possible.

But multi-threading what I have might get to be too hairy code wise, but we'll see I guess.

If Maxscript only uses 1 core, which I'm pretty sure it does? If I could use 2 or 4 cores, I should potentially be able to create around 4400 objects a second if max allows so. Even if I got a 2-3x increase in speed that would be pretty nice.

TheGrak
01-15-2012, 07:01 AM
If Maxscript only uses 1 core, which I'm pretty sure it does? If I could use 2 or 4 cores...

FAIK maxscript uses only 1 core.
Do not know of a way to make maxscript use more than 1 core (maybe dynamically compiling C# code to do such a thing? denisT -> is that possible?).
Could send data to GPUs, but that won't help with most operations in max.
Vray, mentalray, and other programs use all the cores, but that is C++ magic.
So, if you can write C++ magic, max can use all cores.

Kickflipkid687
01-15-2012, 02:04 PM
Yeah, that's kinda of what I figured. I'm learning C#. Maybe I'll attempt to learn C++ at some point. :)

lo
01-15-2012, 02:12 PM
What do you mean that maxscript is limited to one core? You've already shown it can multithread mostly anything using threading.

TheGrak
01-15-2012, 03:42 PM
What do you mean that maxscript is limited to one core? You've already shown it can multithread mostly anything using threading.

We can create threads in maxscript all day long, but we are not guaranteed that the operating system will distribute these threads to all the cores to utilize 100% of the processing power of the computer. Check your task manager the next time you're running a multithreaded script, it won't display 100% usage, whereas if you render with vray or mental ray, all the cores jump up to 100%. That's the C++ magic I was referring to. Technically, I can agree that max can use more than one core, but it won't use 100% of all the cores unless you use C++ magic. I would be happy to be proven wrong. :)

MGernot
01-15-2012, 03:54 PM
There is nothing 'magical' going on with C++.

lo
01-15-2012, 04:06 PM
We can create threads in maxscript all day long, but we are not guaranteed that the operating system will distribute these threads to all the cores to utilize 100% of the processing power of the computer. Check your task manager the next time you're running a multithreaded script, it won't display 100% usage, whereas if you render with vray or mental ray, all the cores jump up to 100%. That's the C++ magic I was referring to. Technically, I can agree that max can use more than one core, but it won't use 100% of all the cores unless you use C++ magic. I would be happy to be proven wrong. :)

myThreads = #()

fn doSomeStuff =
(
for i = 1 to 10000000 do pi * pi * pi
print "done"
)

for i = 1 to 8 do
(
myThreads[i] = dotNetObject "system.componentModel.backgroundWorker"
dotnet.addEventHandler myThreads[i] "DoWork" doSomeStuff
myThreads[i].runWorkerAsync()
)
http://dl.dropbox.com/u/2817180/cpuUsage.jpg
magical!

TheGrak
01-15-2012, 05:23 PM
lo,

i'm getting different results on my windows7 box:
http://www.garrickcampsey.com/media/isItWin7.jpg

are you running xp?
my cores don't pass 50% when processing your example script.
perhaps this is an operating system issue?
Needless to say, your computer does one thing, mine does another.
C'est la vie.

*edit - just noticed you have for i=1 to 8... lemme change that to 4 and see if the cores are used 100%.
*edit - Nope. Hovering around 41% useage now.

lo
01-15-2012, 05:45 PM
tested on windows 7:

max 2009 - 100% usage
max 2012 - 25% usage

wtf, autodesk? :shrug:

TheGrak
01-15-2012, 05:48 PM
I tried creating objects in max with your background worker code, similar to how I was doing it before. However, this no longer works and crashes max almost immediately.

Interesting...


( --WARNING: THIS WILL CRASH MAX
local myThreads = #()
fn doSomeStuff =
(
with redraw off ( box() )
print "done"
)
for i = 1 to 8 do
(
myThreads[i] = dotNetObject "system.componentModel.backgroundWorker"
dotnet.addEventHandler myThreads[i] "DoWork" doSomeStuff
myThreads[i].runWorkerAsync()
)
)


This tells me that the stable creation and modification of objects in max is limited to 1 core.
For code that does not update max's viewports, multithreading is great and can use (most) of the computer. But, for tasks like rigging, moving, animating, creating, etc... max is limited to 1 core. Again, I would be happy to be proven wrong as that would mean all of my scripts could be refactored to use all cores and would be many times faster. I have no problem with being wrong, and I'm not trying to argue with you lo, just trying to write better, faster code.

TheGrak
01-15-2012, 05:49 PM
wtf, autodesk?

Quoted for agreement!

lo
01-15-2012, 06:11 PM
I don't mean to sound argumentative, just trying to get to the bottom of this issue.

Kickflipkid687
01-15-2012, 06:23 PM
Maybe in Max 2013 they will add MultiThreaded Scripting

TheGrak
01-15-2012, 07:03 PM
Maybe in Max 2013 they will add MultiThreaded Scripting

I think that would require a rewrite of maxscript, and the devs have been hesitant to change how maxscript is implemented because such a rewrite has the potential to break literally thousands of scripts and plugins.
So, things are kind of stuck the way they are. Imagine if 2013 was released and didn't work with any previous scripts. The response from the users and 3rd party plugin developers would be epic. And unless AD can quantify in dollars and cents why they would need to rewrite maxscript, they won't. Not enough users care about such a feature (I do), and (as we all know) they have a lot to do already. I don't see stable multithreaded object creation/manipulation being in the next release - but I'd be happy to be wrong.

denisT
01-15-2012, 07:05 PM
I tried creating objects in max with your background worker code, similar to how I was doing it before. However, this no longer works and crashes max almost immediately.

This tells me that the stable creation and modification of objects in max is limited to 1 core.

it's not limited to 1 core, it's limited to one thread. all viewport, all scene operations should be in the same thread.
mxs isn't limited by number of cores to use. it might use all available.

TheGrak
01-15-2012, 07:18 PM
it's not limited to 1 core, it's limited to one thread. all viewport, all scene operations should be in the same thread. mxs isn't limited by number of cores to use. it might use all available.

But it doesn't. And I'd like it to.
For example, this simple code...
(for i = 1 to 1000 do box())

...spiked 1 core for a brief period of time (and not even 100%):
http://www.garrickcampsey.com/media/notAllCores.jpg

If everything is in one thread, then is it up to the operating system or 3dsmax.exe to decide how many cores are used? We need some clarity here. I would expect 3dsmax.exe to spike all cores for a brief moment to create the boxes, but that doesn't happen. Why?

denisT
01-15-2012, 07:32 PM
you are talking about one specific function here - create primitive... it might not support multi-core. but it doesn't mean that mxs can't use multi-core.

Kickflipkid687
01-15-2012, 09:09 PM
Well I know the Itoo Software Plugin can use Multicores, and u can say how many.

It can create like 500K "objects" per second apparently. But Im not sure how they are going about that.... Although the objects are actually elements in a mesh.

CGTalk Moderation
01-15-2012, 09:09 PM
This thread has been automatically closed as it remained inactive for 12 months. If you wish to continue the discussion, please create a new thread in the appropriate forum.