1.15.2 Conceptual question about timers and CPU performance

Discussion in 'Spigot Plugin Development' started by AntivirusDev, Apr 3, 2020.

  1. I am not new to programming, but I have only a bare minimum understanding of how code affects hardware performance.

    Suppose there are 3 synchronous tasks to be performed every second which require moderate to heavy CPU processing power (i.e. redstone detection, cleaning up light in the world, calculations, etc.). I know that the absolute performance will depend on what the tasks actually are, but for this question, assume that these tasks can be feasibly performed each second and that I am speaking of relative performance, not absolute.

    Would it be more CPU-efficient to:

    (a) run all three tasks in a single timer
    Code (Java):
    Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable(){
        task1();
        task2();
        task3();
    }, 0L, 20L);
    (b) run each task in its own timer
    Code (Java):
    Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable(){
        task1();
    }, 0L, 20L);
    Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable(){
        task2();
    }, 0L, 20L);
    Bukkit.getScheduler().scheduleSyncRepeatingTask(plugin, new Runnable(){
        task3();
    }, 0L, 20L);
     
    or does this not have a meaningful impact on performance?

    Also, I don't care about RAM usage. I am strictly looking for how this may or may not affect the CPU.
     
  2. its the same. it doesnt matter how you perform the code. code will use the same cpu no matter hwat you do lol. tasks only help the main game loop from pausing too long and pausing the server indefinitely. it doesnt change the cpu usage
     
  3. While this generally spoken is true, I wouldn't be so fast on saying it doesn't make any difference at all.

    Very interesting question. There are some things to consider, for example how exactly Runnables are included into the Bukkit / Spigot API, but I would say this is more a question onto how Runnables and therefore the Thread is exactly handled by the running JVM.
    Obviously, since everything is still synchronous, you can't magically expect asynchronous thread optimization levels here and too much difference in the outcome, as if every task was run in it's own thread, however:
    My assumption would be, that everything inside a Runnables run() method is handled as one Unit and therefore must be executed in sequence without stopping. Putting everything into one Runnable, would therefore "force" the Thread and therefore the server to run this method before doing anything else - and this is where Bukkit / Spigot API might intervene in terms of doing some other things in between running the next task.

    But I could assume and make speculations about this all day, while the only true way to test / verify this is, by profiling both scenarios yourself.
    Let it run with everything inside one task once and the other time create a task for everything and see what the profiler will say about the timings.

    Other than that, you might want to look into what things happen inside Bukkits / Spigots scheduling for tasks, maybe @md_5 could give a small insight on that?
     
    • Agree Agree x 1
  4. Intuitively, it would make sense for separate timers to be slower. If you have to grab three items from a table, it would take longer to walk over, grab one item, and walk back three times instead of walking over once, grabbing all three items, and walking back.

    I don't know Java well enough to know exactly how the JVM handles the internals of the code, but I think your guess is pretty realistic. I was thinking that perhaps Spigot executes each timer as its own sub-process. If each task runs in some constant time, running all of the tasks in one timer would certainly result in O(a + b + c) time, but running each task in its own timer would probably have a slower time (i.e. O(a + b + c + d)), since the CPU must process three times as many timer executions each second. Just a guess though.


    And yeah it would be nice for @md_5 to clear things up.
     
  5. drives_a_ford

    Moderator

    Main things to consider:
    • There's always overhead in object creation
      • In the first example you're creating 3 instances of Runnable
      • In the second example, you're creating 1 instance of Runnable
      • The overhead might not easily be measured, but it'll be there.
    • You can take a look at how the scheduler works at CraftScheduler

    EDIT:
    I really don't think tagging md here is at all necessary.
     
    • Agree Agree x 1