TPF in a Callable?

Is there any way to measure how much in-game time is passing during the time a Callable is running? Should I just do anything time-sensitive on the render-thread? I’m thinking of adding each tpf value to a local float and using it in the next run of the Callable, but it may actually just be easier to port anything time-sensitive to the update loop.

@vinexgames said: Is there any way to measure how much in-game time is passing during the time a Callable is running? Should I just do anything time-sensitive on the render-thread? I'm thinking of adding each tpf value to a local float and using it in the next run of the Callable, but it may actually just be easier to port anything time-sensitive to the update loop.

It really depends on what you are actually trying to do.

In general, reliable inter-thread communication incurs an overhead, though some overheads are unavoidable.

  1. You could place an updated tpf value in a member variable and read that.
    I’m not 100% sure about the details, but I think it needs to be transient to force the JVM into forfeiting any optimizations that could be gained by delaying the update to the member variable. I’d probably go for an AtomicFloat just to make sure it works as intended, can’t have timing fail just because a new Hotspot decides to start optimizing things differently.

  2. Alternatively, you could split the Callable into multiple steps, each of them running as their respective time has come. The result of such code refactoring usually results in hard-to-maintain code.

  3. Finally, there are inter-process mechanisms that can be embedded into the normal workflow, such as Futures. Let the render loop generate Futures that get passed to the Callable, and have the render loop fill these Futures in as the corresponding time stamp has passed.

Variants 2 and 3 can be optimized for the case that the number of waiting “waiters” (Callables/Futures) increases: Put the waiters into a list that’s sorted in ascending time-to-run order. The render loop then does not need to iterate over the full waiter list, it can stop at the first entry that’s not yet eligible to run.
I’d start giving this serious thought if performance is an issue and the number of waiters could become more than a few hundred items (just “start worrying”, it’s not necessarily an issue at that threshold yet).

In general a callable is only called once (through enqueue - not through scheduling) and to see how much time it has taken just record system.nanoTime at the start and end. There are all sorts of approaches (some simple, some not so simple) to then aggregate the information.

You will most likely get better results (at least to start with) just using the profiler built into the SDK though. That will tell you where all the program’s time is being spent in an unbiased way.

Just be aware of what is “normal” before you start investigating things too much though. For example your profile may well show a lot of time updating the scene graph - but in general that is to be expected.

I think there is a misunderstanding. I’m quite sure that OP wanted to use tpf to determine how big step has to be done in game logic - performance part is not coming into picture at all. time-sensitive = having to react to game time, rather than time-sensitive=performance-critical.

So a tpf value to an enqueued callable?

Sounds like there is an architectural hole if that is needed.

…and this is why I’m curious what the OP is actually trying to do because everything else is just a wild guess on our part.

I was working on threaded AI. The idea behind the AI is quite simple. If they see the player, they run at it. If they previously saw the player, but don’t now, they move to the last place they saw the player. If they hear a sound, they move towards the sound’s location and investigate it. Otherwise, they just wander aimlessly. The update code kicked up some lag with lots of zombies in one scene, so I started trying to thread it. However, the TPF was needed to subtract from the interest time on certain things the zombie is tracking, and some other things as well. In the end, I attempted to port the time-sensitive things to the render-loop, and the other stuff to a separate thread. This ended up pretty much wrecking the code and making it mostly useless so I’m just starting from scratch on it. >.>

Sounds like you were using tpf to accumulate time when clock time would have maybe been fine. Though I’m guessing you have it all sorted out now or at least have a plan?

It wasn’t clear from your last post if you are still looking for help or have just picked a different direction that you already understand.

@pspeed I’ve restarted on the whole AI code, this time with a better, much more thread-safe idea in mind. So far it’s more or less working.

I think a good sign that your callable shouldnt be used is if it contains more than 1 or 2 lines of code. You might want to consider just using the update loop of the AppState/Control and enqueue to yourself with a private member variable called flag. (with public getters/setters)

then in your update loop just say

if(flag){ blah blah blah*tpf}

now any thread can invoke the code behind “flag”

This also has the benefit of being a lot more clear as to the order your code is executed, and what code you have running on the main thread in general. In my projects I generally never use callable unless I’m trying to do a quick expirement or if I need to run a small snippet of code on the main thread once.