ScriptT3D: Working on a Refactor/Rewrite
by Demolishun · 11/25/2012 (3:03 am) · 8 comments
ScriptT3D as is works for T3D 1.2 and most likely will work with T3D MIT. However, there are some performance issues I would like to target with this rewrite. SWIG has a mode for producing a less cumbersome intermediate Python class. Meaning there is no Python class in the middle to add to function call latency. There are also some other speedups I can take advantage in this mode. One of those is the function call lookup Python does on an object when it calls an object method. If you use "slots" then you can speedup certain calls which is one reason this type of interface was designed.
I have put on hold some other sub projects in my main project to tackle this interfacing challenge. I really want my code interface to be extremely fast with little overhead in function calls. I also want to tackle something that could revolutionize the interface when it comes to speed.
In my earlier blogs you may have seen some of my posts about PyV8. This is Python wrapper for the V8 Javascript engine. This module is an amazing piece of work. Not only can you run Javascript inside of Python, but each execution context can have its own context. This means user side scripting, internal scripting, server scripting, etc can all run in their own little JIT compiled worlds. However, it does not end there. Go to this page about PyV8 and read about how the interface works in the first paragraph. Let that sink in.
If you didn't get what they were talking about they are saying that they are looking at ways to convert Python to Javascript so it can be compiled. Now, I don't believe that the Advogato PyV8 project is related to the PyV8 project project officially. However the implications are pretty amazing. Now what would be interesting to me is not the ability to compile the Python code in its entirety, but only where it touches the interface to the T3D library. If this can be accomplished then Javascript could literally be interfaced to the engine calls of the T3D engine at compiled code speed. Now, I am a little unsure as to where the official PyV8 project is in that regard, but I can still gain speed benefits even without the compiled Python code.
Here is a sample of PyV8 interfacing code:
Obviously this is a very simple and probably erroneous example, but it should illustrate what I am trying to show. The scriptT3D object is a Python library which is imported. I then create an execution context with exposed Python functions. These calls are made available to the Javascript that is run when you perform the js.eval call to evaluate your Javascript code. What does that mean? It means you can very directly provide the T3D API via Python to Javascript and still get the benefits of the Python language as well. If the proper functions are exposed this means that all the current Torque Script code could be converted to Javascript and would then be JIT compiled at runtime. I have converted some Torque Script by hand to Javascript and the conversion is fairly straightforward. I would say it could be done automagically with a well written parser. One of the issues with moving to a different scripting language is "What do I do with my existing codebase?" Well if there was a way to convert to Javascript then it would not be a really big issue except where the code uses TS specific features. Those might have to be converted by hand.
I know there is interest in getting ScriptT3D working with T2D. By taking this route and updating ScriptT3D for the MIT version I am doing part of the necessary to make this available to T2D. The reason this is true is because there are features in the T3D codebase that I rely on. In order to port those over to T2D I have to use the MIT version. There is also the matter of GGs pending update of the T2D code. I am hoping that kicks out soon so that I don't have to write the T2D interface twice. So bear with me if you are waiting for this for T2D.
As of today I have successfully started T3D MIT with basic functions for initializing, running the main loop, and exiting program under Python control. I am using the new "-builtin" option to do this. So far it just seems to "work". My next steps are looking into custom Con::exec calls that not only call the exposed functions from the console, but also remember the call lookup so subsequent calls do not need to do any lookup. I think as long as I stay in the main execution thread this should not be an issue. This was already a limitation on my existing interface so that is not an issue. This combined with the "slots" interface for Python we should get faster calls than the console calls from TS. Then I can give the Javascript context access to that interface and potentially significantly speedup the engines scripting language. There are some callback issues from objects, and Python to TS callbacks can solve those, or even a redirection to Python for those calls would take care of that. Not sure what will be best there.
Edit:
Note:
I am not replacing TS in any way. So one could speed up critical sections of the scripting code without touching the rest of their TS codebase. I am just supposing ways to speedup the entire engine if a TS to Javascript converter were to be written.
Any questions?
Bueller...Bueller...Bueller...
I have put on hold some other sub projects in my main project to tackle this interfacing challenge. I really want my code interface to be extremely fast with little overhead in function calls. I also want to tackle something that could revolutionize the interface when it comes to speed.
In my earlier blogs you may have seen some of my posts about PyV8. This is Python wrapper for the V8 Javascript engine. This module is an amazing piece of work. Not only can you run Javascript inside of Python, but each execution context can have its own context. This means user side scripting, internal scripting, server scripting, etc can all run in their own little JIT compiled worlds. However, it does not end there. Go to this page about PyV8 and read about how the interface works in the first paragraph. Let that sink in.
If you didn't get what they were talking about they are saying that they are looking at ways to convert Python to Javascript so it can be compiled. Now, I don't believe that the Advogato PyV8 project is related to the PyV8 project project officially. However the implications are pretty amazing. Now what would be interesting to me is not the ability to compile the Python code in its entirety, but only where it touches the interface to the T3D library. If this can be accomplished then Javascript could literally be interfaced to the engine calls of the T3D engine at compiled code speed. Now, I am a little unsure as to where the official PyV8 project is in that regard, but I can still gain speed benefits even without the compiled Python code.
Here is a sample of PyV8 interfacing code:
import scriptT3D
import PyV8
class js_global(PyV8.JSClass):
def exec(self, name, parms):
scriptT3D.exec(name, parms)
jsg = js_global()
js = PyV8.JSContext(jsg)
js.enter()
...
js.eval("exec("echo","hello T3D from Javascript");")Obviously this is a very simple and probably erroneous example, but it should illustrate what I am trying to show. The scriptT3D object is a Python library which is imported. I then create an execution context with exposed Python functions. These calls are made available to the Javascript that is run when you perform the js.eval call to evaluate your Javascript code. What does that mean? It means you can very directly provide the T3D API via Python to Javascript and still get the benefits of the Python language as well. If the proper functions are exposed this means that all the current Torque Script code could be converted to Javascript and would then be JIT compiled at runtime. I have converted some Torque Script by hand to Javascript and the conversion is fairly straightforward. I would say it could be done automagically with a well written parser. One of the issues with moving to a different scripting language is "What do I do with my existing codebase?" Well if there was a way to convert to Javascript then it would not be a really big issue except where the code uses TS specific features. Those might have to be converted by hand.
I know there is interest in getting ScriptT3D working with T2D. By taking this route and updating ScriptT3D for the MIT version I am doing part of the necessary to make this available to T2D. The reason this is true is because there are features in the T3D codebase that I rely on. In order to port those over to T2D I have to use the MIT version. There is also the matter of GGs pending update of the T2D code. I am hoping that kicks out soon so that I don't have to write the T2D interface twice. So bear with me if you are waiting for this for T2D.
As of today I have successfully started T3D MIT with basic functions for initializing, running the main loop, and exiting program under Python control. I am using the new "-builtin" option to do this. So far it just seems to "work". My next steps are looking into custom Con::exec calls that not only call the exposed functions from the console, but also remember the call lookup so subsequent calls do not need to do any lookup. I think as long as I stay in the main execution thread this should not be an issue. This was already a limitation on my existing interface so that is not an issue. This combined with the "slots" interface for Python we should get faster calls than the console calls from TS. Then I can give the Javascript context access to that interface and potentially significantly speedup the engines scripting language. There are some callback issues from objects, and Python to TS callbacks can solve those, or even a redirection to Python for those calls would take care of that. Not sure what will be best there.
Edit:
Note:
I am not replacing TS in any way. So one could speed up critical sections of the scripting code without touching the rest of their TS codebase. I am just supposing ways to speedup the entire engine if a TS to Javascript converter were to be written.
Any questions?
Bueller...Bueller...Bueller...
About the author
I love programming, I love programming things that go click, whirr, boom. For organized T3D Links visit: http://demolishun.com/?page_id=67
#2
11/25/2012 (3:50 pm)
Frank, awesome! Don't know if you got my email, but I was bugging you about "ScriptT2D" in essence. I'm thrilled to hear that you are considering it.
#3
If you want to get a hold of me you can: xxxxxxxxxxxxxxxxxxxxxxxxxxxx.
11/25/2012 (3:59 pm)
@Kevin, I did not get an email, but I did see your post on the ScriptT3D resource page: www.garagegames.com/community/resources/view/21706/2#comment-188502. If you want to get a hold of me you can: xxxxxxxxxxxxxxxxxxxxxxxxxxxx.
#4
Random question: do you reckon it'd be possible to embed a Python engine in Torque? So that end-users wouldn't have to depend on having the right version of Python installed to run Torque. I realise this is probably insane. Thought I'd ask anyway!
11/25/2012 (4:26 pm)
I've never tried to get this working, but now I'll probably wait until I hear more about this rewrite! You've done awesome work so far, and I'm looking forward to seeing where it goes.Random question: do you reckon it'd be possible to embed a Python engine in Torque? So that end-users wouldn't have to depend on having the right version of Python installed to run Torque. I realise this is probably insane. Thought I'd ask anyway!
#5
Early on when learning about Python I found that it really is a language you should extend rather than embed. Here is a link that talks about this issue: docs.python.org/2/extending/.
When extending Python, in order to not require a version of Python be installed on the end users system, you use a packager program. One very popular one for Windows systems is py2exe. This will package everything required for your app in a nice exe. This includes any Python runtimes. It can also put all your libs in a zip file external from the exe. The external zip file is especially useful if you have LGPL libraries and need to give the end user the ability to replace those libs if they so desire (this is a requirement to be in compliance with the LGPL).
Blender has Python embedded (Blender can also be compiled as an extension ref). So that is an example of one app that has embedded Python. There are a lot of other apps that extend, but you would never know it unless you knew what you were looking at. One example is Minions of Mirth.
Some of the work I have done for my customers requires me to package the Python app in an exe for the end user. The end user only gets compiled pyo files packaged with Python. So they only get byte code. In a sense it is similar to running a Java app except you don't have to install anything.
Thanks for the kudos!
11/25/2012 (4:58 pm)
@Daniel,Early on when learning about Python I found that it really is a language you should extend rather than embed. Here is a link that talks about this issue: docs.python.org/2/extending/.
When extending Python, in order to not require a version of Python be installed on the end users system, you use a packager program. One very popular one for Windows systems is py2exe. This will package everything required for your app in a nice exe. This includes any Python runtimes. It can also put all your libs in a zip file external from the exe. The external zip file is especially useful if you have LGPL libraries and need to give the end user the ability to replace those libs if they so desire (this is a requirement to be in compliance with the LGPL).
Blender has Python embedded (Blender can also be compiled as an extension ref). So that is an example of one app that has embedded Python. There are a lot of other apps that extend, but you would never know it unless you knew what you were looking at. One example is Minions of Mirth.
Some of the work I have done for my customers requires me to package the Python app in an exe for the end user. The end user only gets compiled pyo files packaged with Python. So they only get byte code. In a sense it is similar to running a Java app except you don't have to install anything.
Thanks for the kudos!
#6
11/26/2012 (7:35 am)
Thanks Frank - I had lost track of that comment.
#7
So, I have a few more things to add to the next iteration of ScriptT3D. Mostly I need address the callbacks and some of the utility functions. The API is changing because the previous versions I didn't like the way the functions were laid out. Mostly due to the intermediate Python classes that are now non-existent.
Another issue I am tackling is whether or not to include exporting entire Python objects into the console. I will keep exporting individual functions, but I am undecided on doing entire objects. Mainly because I am really not sure how useful it really is and because it adds complexity. The same end can be achieved by picking and choosing functions to export into the console from the Python side, rather than letting the interface parse an entire object. The only thing unique exporting the whole object brought was native data typing for attributes and attribute access.
My goal of getting as close to the metal as possible was partially successful. The problem is a conceptual one that I am not sure can be solved with the tools at hand without a lot of brain damage. I wanted the Python side to be able to access globals, functions, and simobjects by using dot(.) notation. So that means it requires dynamic lookup of these items. Once the item is identified I wanted to make it so the access was faster the next time by having it remember the vector. So I really wanted a meta programming solution, and I wanted it to be fast. Well, after doing a lot of searching I finally determined that I could get most of what I wanted.
For functions I ended up using 3 intermediate Python classes to handle the dynamic lookup of functions from T3D. These objects will create a new vector and use builtin functions (written in C) to process command line args. This wraps the function (lambda) into an attribute on the lookup object. So the next lookup is faster and it just has to execute the function. On the T3D side the first call does a lookup and verifies the console vector. For global functions it remembers the name space so subsequent calls do not have to look up the namespace. For class methods I was able to reduce the call overhead a bit more by avoiding some expensive operations after the first call. I also made the call more robust by being able to detect object deletion via registerReference and unregisterReference. When I process a function on a SimObject I store the reference to the SimObject. On subsequent function calls I use that stored reference. If you don't tell the SimObject you made a reference to that object it could get deleted and you would never know. Then you would try to use a bad pointer and crash. By registering with the SimObject it will update that reference to NULL upon object deletion. So all the code has to do is check for NULL for that object.
Anyway, after last night I am much closer to completing this.
11/26/2012 (2:24 pm)
You know you are in the groove when you only intended to work on something a "little". Then you work from 11pm to 8am because you want to do "one more thing". The next day you look at the code and realize it is way better written than any previous iteration, and it is nearly finished. So, I have a few more things to add to the next iteration of ScriptT3D. Mostly I need address the callbacks and some of the utility functions. The API is changing because the previous versions I didn't like the way the functions were laid out. Mostly due to the intermediate Python classes that are now non-existent.
Another issue I am tackling is whether or not to include exporting entire Python objects into the console. I will keep exporting individual functions, but I am undecided on doing entire objects. Mainly because I am really not sure how useful it really is and because it adds complexity. The same end can be achieved by picking and choosing functions to export into the console from the Python side, rather than letting the interface parse an entire object. The only thing unique exporting the whole object brought was native data typing for attributes and attribute access.
My goal of getting as close to the metal as possible was partially successful. The problem is a conceptual one that I am not sure can be solved with the tools at hand without a lot of brain damage. I wanted the Python side to be able to access globals, functions, and simobjects by using dot(.) notation. So that means it requires dynamic lookup of these items. Once the item is identified I wanted to make it so the access was faster the next time by having it remember the vector. So I really wanted a meta programming solution, and I wanted it to be fast. Well, after doing a lot of searching I finally determined that I could get most of what I wanted.
For functions I ended up using 3 intermediate Python classes to handle the dynamic lookup of functions from T3D. These objects will create a new vector and use builtin functions (written in C) to process command line args. This wraps the function (lambda) into an attribute on the lookup object. So the next lookup is faster and it just has to execute the function. On the T3D side the first call does a lookup and verifies the console vector. For global functions it remembers the name space so subsequent calls do not have to look up the namespace. For class methods I was able to reduce the call overhead a bit more by avoiding some expensive operations after the first call. I also made the call more robust by being able to detect object deletion via registerReference and unregisterReference. When I process a function on a SimObject I store the reference to the SimObject. On subsequent function calls I use that stored reference. If you don't tell the SimObject you made a reference to that object it could get deleted and you would never know. Then you would try to use a bad pointer and crash. By registering with the SimObject it will update that reference to NULL upon object deletion. So all the code has to do is check for NULL for that object.
Anyway, after last night I am much closer to completing this.
#8
11/28/2012 (10:50 pm)
Another inch forward! 
Torque Owner David Robert Pemberton
www.deadlyassets.com