3 ms·
I'm currently working on a game engine that is a mix of C++ and Io. Here are some things I learned: - By far the most time spent in cross-language calls is in
by phaedrus 15y ago
I'm currently working on a game engine that is a mix of C++ and Io. Here are some things I learned:
- By far the most time spent in cross-language calls is in functions that take two integers and return a boolean. What's up with that? Enums! The C++ 3D engine I'm using uses enums, and my binding library represents enums in script with an object that overloads the == and != operator. I really need to get around to optimize that.
- Garbage collection is not slowing me down (much). I only spend 10% of my time in GC functions for the script (and I have a lot of scripts running). Once I accidently left a call to "Collector collect" in my game loop, then GC took 40%. But, Io's garbage collector didn't make the game jerky, even with a collect in my game loop.
- There are some things that are better to write in C++ than in script. I don't mean, better from a performance standpoint, I mean there are some things that are quicker to implement in C++ than in script. I started with the idea, "I'm going to write everything in Io and optimize later" and I made a lot of bindings to C++ function, both high and low level, to support that goal. But some things like dealing with triangle meshes or bit twiddling do better in C++. Scripting is faster to implement ideas in for so many things, but in places where C++ shines it is still faster to do in C++.
- When you make memory errors in C++ they generally cause a crash in Io's garbage collector. I once lost a month debugging and instrumenting every line of Io's garbage collector to figure out why it was freeing objects still in use only to discover the actual problem was a C++ library was squirreling away pointers to objects it didn't own and reading through the pointers later when the objects were not guaranteed to still be live (even in a pure C++ program).
- The chain of events where a memory error in C++ blows up in the script garbage collector goes like this: a C++ object gets deleted, but another C++ object still has a pointer to it. When the script asks all the C++ objects to mark the objects they're using (Io uses a mark-sweep collector), the live C++ object holding a rogue pointer calls the mark() function on a deleted object. But the call succeeds because the deleted object hasn't been completely overwritten. But the the corpse of the deleted object in turn holds references to script objects which it has not been marking previously because this C++ object didn't exist anymore when the sweep started. The script objects it used hold have just been collected, but it still knows their old addresses. It asks the script interpreter's garbage collector, hey, go mark script objects for me at these addresses. The script's garbage collector then tries to mark script objects that aren't live, follows the graph of dead script object's references to other script objects, also dead, and eventually reaches a bum pointer. Blammo! The GC just blew up deep in a forest of script objects, and the poor script language gets blamed for something C++ did.
- outworlder 15y agoThis is interesting. What made you adopt Io instead of Lua? Or was it dictated by the engine?
- phaedrus 15y agoFirst, because I knew Io and not Lua. I had gotten interested in Io years before. Io was actually my first dynamic language, not Python or Ruby or Javascript. I stopped using Io for a few years when I moved on to other things, but _why's article "Io Has a Very Clean Mirror" brought it back to mind. I seriously considered Lua but I felt object orientation needs to be incorporated very fundamentally in a language. I had previous experience with mIRC which shares Lua's idea of "everything is a table", and while mIRC is no doubt productive I felt tables would be a step backwards compared to Io's objects. At the time I started, the C/C++ binding frameworks for Lua were not as mature as they are now, not as numerous, or I simply wasn't aware of them. From a language aesthetic standpoint my decision came down to Io, or Ruby, but I looked at the C Ruby source code and it didn't seem very suited to embedding. For instance the official implementation of Ruby had some dependence on global/static variables which precluded the idea of running multiple instances of the VM in a process. Io allows this, although I haven't used it yet; I plan to use it to support threading, one VM per thread. And basically the Ruby interpreter codebase wants to own main() and call my code as a library; I wanted to own main() and call the interpreter as a library. Io supports this, and it's codebase is smaller.
- xsmasher 15y agoCan you elaborate on the bool vs enum point? I don't get it.
- jmaygarden 15y agoHe's making a C API call to essentially compare two integers instead of just comparing them directly in Lua. You can fix that by pushing the enum values to Lua as numbers instead of userdata with metatables for comparisons.
- 15y ago