SkookumScript from its beginnings was always intended to be paired with C++. You can write awesomely fast code with C++ yet SkookumScript and C++ together are a much more powerful set of tools than C++ alone.
The key here is that they are different tools and they are each better at some jobs and worse at others. This is true for C++, SkookumScript and Blueprints in Unreal Engine 4.
C++ is likely to have greater performance crunching a lot of numbers than SkookumScript and perhaps Blueprints that are compiled to C++ might be faster than SkookumScript too. You would definitely write a rendering system in C++ and not SkookumScript and definitely not Blueprints either.
As far as performance goes, SkookumScript has three major things going for it:
- the awesome speed of doing nothing by using concurrency
- the best use of SkookumScript is for stage-direction
- good old raw optimization
Great at doing nothing
You might think that after writing a bunch of SkookumScript commands for a project that you might go back and rewrite some in C++ so that key or particularly slow commands would be faster. In practice this happens very rarely - SkookumScript is fast enough and this isn’t usually a concern. However somewhat surprisingly, some C++ commands are rewritten in SkookumScript to make them faster. Huh, what!?
This is because it is so easy to write concurrency with SkookumScript that you can effortlessly spread work over time. For example, if you have an expensive test that is occurring on a bunch of game characters every frame and you really only need the test to react at “human speed” - say every third of a second - rather than every frame then you can write code like this:
Bam! You just spread that expensive test over time. Assuming your game is running at 60 frames per second, now rather than being called 60 times per second it will be called approximately 3 times. You’ve just created a 20X speed increase! Painstaking raw optimization can only get you so far - doing nothing is the king of speed.
There are many other ways that concurrency can be used to increase performance though this is a good simple example.
Designed for stage-direction
The ideal way to use a scripting system is to make high-level decisions and to have the game engine do most of the heavy lifting. Set a bunch of engine commands in motion, wait for results and events, perform some quick logic and set new engine commands in motion, repeat. For example, send some bad guys run over to this spot around the corner, when the player walks through this trigger area, then queue up the dramatic music and have the bad guys run to positions around the player and then attack.
The ratio of work (or processing cycles) done by the scripting system as compared to the engine should be very small. When the scripting system is waiting for the engine to complete the tasks assigned to it, the scripting system is ideally again doing nothing.
If the scripting system is doing a lot of work crunching numbers and doing a mesh deformation or calculating intersection points of a ray and a convex hull - you are probably not using it as intended.
In the case of Unreal Engine 4 - it is a huge beast of an optimized C++ engine with almost five thousand commands exposed to the scripting system. Most of the heavy lifting is going to be with those commands - not the scripting system. Thankfully, there is a massive team of men and women constantly optimizing and improving those commands. If you are so inclined you can also add your own engine or custom project commands to the mix. There are so many commands that everything you need may already be there.
In custom engines there will be a mix. Some people will write C++ code, some will write SkookumScript and many will do a mix of both.
SkookumScript is designed to work together with C++ and Blueprints with UE4.
SkookumScript is optimized
SkookumScript is compiled and not interpreted. As much work is done up-front as possible rather than during runtime. Everyone has supercomputers in their pockets in the form of their phones - there is no reason not to have the awesome might of the computers at our disposal to be put to work with our programming languages.
SkookumScript does not use a virtual machine (VM) like many scripting languages. The compiled scripts are stored in platform independent binaries and are loaded into runtime memory as data-driven optimized C++ data structures. This gives SkookumScript near native C++ speed.
The SkookumScript runtime is written in C++ and has been refined and evolved over time through repeated profiles and optimizations. A somewhat recent optimization pass increased the speed of SkookumScript by approximately 2.5X. We are big believers in profiling code to find and reduce bottlenecks - you never really know for sure unless you check.
The bindings to Unreal Engine 4 are created by crawling through the many thousands of lines of UE4 C++ code looking for the calls exposed to the Blueprint system and generating corresponding C++ bindings for SkookumScript.
Other performance considerations
Runtime speed only scratches the surface of performance considerations.
Great workflow and fast turn-around
It is one thing to run fast - it is another to allow a really quick turn-around time and to rapidly create a great product. This is the key area around which SkookumScript is designed and its greatest priority.
Being able to run arbitrarily complex commands live while the game is running on any platform is huge. The built-in game constructs such as concurrency and context from the game world editor allow you transfer your great ideas intact over to the computer without needing to twist it into computerese. This massively maximizes our most limited resource which is our puny human brains. Step-wise debugging takes concurrency into account and locks to specific game objects when the same code can be used by multiple game objects. Then the icing on the cake is that SkookumScript can modify code while a game is running without needing to stop, rebuild, play to the same progression point, hit all the same events and retest. The time saved can be a game-changer and completely change your workflow and keep you in the flow of your creative process. Some teams just starting to use SkookumScript say that they saved hours in their first week.
Also worth noting is that the SkookumScript compiler is lightning fast. The over eight thousand scripts of an entire open-world game written using SkookumScript compiles in about three seconds. This was on machines from 2010.
All of this allows for much more polish and vastly better games.
SkookumScript excels at writing efficient concurrent code though why stop there when you can be parallel across a team too. SkookumScript has evolved on teams and is designed to be able to easily scale from small teams (even a solo hobbyist) to a huge AAA project without everyone bumping elbows. SkookumScript code files integrate well with version control. You can easily transfer script snippets over chat programs and email.
See some more advantages of using text based scripting over a visual node graph.
SkookumScript has been designed and extensively optimized to have a small memory footprint and to work efficiently with memory. All the memory used by SkookumScript comes from preallocated pools and reused data-types for predictability and speed. Automatic reference counting (ARC) is used for deterministic memory handling rather than a more heavy-handed garbage collector that runs at unpredictable times.
Just like the best way to be fast is to do nothing, the best way to have a small footprint is to not be loaded in memory - SkookumScript can use demand loading and unloading of classes. You can load and unload SkookumScript classes based on the game progression, geography, etc. If there are a bunch of missions that require a special vehicle or inventory item that you don’t have then don’t have those missions loaded in memory.
It is possible that some specific sequences of commands in UE4 Blueprints converted to compiled C++ might be faster than SkookumScript, though we would have to profile it to be sure. SkookumScript always had the potential to be slower than well written and optimized C++ though using both is still superior to just C++. The main problem comparing SkookumScript and Blueprints is that they are different tools and you don’t ideally do the same work with them. Which is faster - a hammer or a saw? Both are faster than each other at different tasks and create higher quality work at some tasks rather than others though you create better work with both in your toolbox.
It is fantastic when other tools are improved. They make our craft better overall.
SkookumScript is a pretty amazing tool and we will continually improve it and make it a much prized part of your game development process.
EDIT: Since the original question was about Sk vs. Nativized BP performance, check out some benchmarks a year after this post.
p.s. Thanks @Gigantoad for all the references and recommendations for SkookumScript on the UE4 forums.