Pulse Optimizations

For those using the Pulse Unity Asset,
(Or Java, or C#, or Python)

A few users have reported back that integration of the Unity Asset into their game had unexpectedly slowed down their game a bit more than anticipated. The most significant impact was on AR headsets.

Now there is a cost associated with Pulse, it does need to calculate (mostly solving a few large matrices) every 0.02 seconds, which should calc in the order of few ms. That will affect frame rate to some degree, but it should really be as minimal as it can be.

So, I started some profiling investigation and found a few places and usability issues to optimize.

Inspecting the mechanism to push/pull data to/from C++ to C# was the fist place to look.
The most immediate impact was to change the internal DataRequest container from map<string,value> to a vector so I could add index lookup support rather than perform string compares in getting data from pulse and populating the requested data array to pass back to C#. Moving to index access rather than using a string key into a map really improves performance. The down side to this is that its going to be more work if/when we support the ability to dynamically add/remove (mostly remove) data requested from Pulse in the C# API. (Let me know if you would need that kind of feature)

The next thing learned is that changing the timeStep in the PulseEngineDriver script is not a good idea.
What this did, was just delay calls into Pulse until the timeStep threshold was met, then call Pulse a bunch of times to catch up to the current time. (ex. Setting the timeStep to 0.1, would actually have a single frame call Pulse 10 times to advance 0.1s, dramatically slowing down the overall game frame rate with very long frames every 0.1s)

The optimal way for Pulse to run, is to always calculate at its timeStep, 0.02s. This will ensure that, on average, when pulse does need to calculate in a frame, it will calculate one time step. You may get a frame that does have to calculate 2 frames here and there due to mathematical precision, but in general, this effectively spreads the load across multiple frames, and keeps the frame rate high.
I also increased the precision in the scripts from float to double to to reduce the mathematical precision issues in calculating when Pulse needs to run which keeps most, if not all, pulse calculations to 1 calc call per frame.

I have removed the timeStep variable, and replaced it with a sampleRate variable.
The PulseEngineDriver will always calculate Pulse at a rate consistent with its timeStep in order to spread calculation load, and keep frame rate high.
But the sampleRate controls how often the PulseEngineDriver pulls data from C++ into C#, giving you a bit more FPS if you really need it AND you don’t that much data. (Note that Pulling data is much quicker due to the above mentioned optimization, but every little bit can help in gaming!)

So in my previous example, if you only want to get data every 0.1s, you can do so without negatively impacting the frame rate. But do note, that if you are drawing waveforms, like the VitalsMonitor does, you should be very aware that sampling at larger time steps will result in bad waveforms.


This is using a sample rate of 0.1s

If you are not drawing waveforms, and only displaying numerical values, like values on the right side of the vitals monitor, you can sample at higher rates to get a frame rate boost if you need it

Note these changes will also improve performance of Pulse being used in Java, C# and Python, (and C++ if you are creating CSV files)

Expect this update in a 3.1 release (of Pulse and the Unity Asset) after we finish up our new hemorrhage improvements.
If you would like a build of the unity asset with this update, please let me know.

And please let us know if there are any other oddities you are noticing with Pulse.

Thanks!

1 Like

Nice investigating work!