Thread Safety with java.math

Discussion in 'Plugin Development' started by MrAwellstein, Apr 30, 2014.

Thread Status:
Not open for further replies.
  1. Offline


    Is it safe for me to use java.math in the main thread? I need to call tangent and cosine twice per tick, and I thought I read somewhere a while back that they take a slight bit of time to calculate, that could cause issues.
  2. Offline


    MrAwellstein I believe it is fairly heavy if called every few ticks, it could cause some thread-locking or lag, you could try running an async task that does the calculations then schedules a sync task that runs the rest of the code
  3. Offline


    They are totally thread safe. There is no state being stored, so there is absolutely no reason for them not to be. What you would actually get is thread-locking, where the main thread gets stuck on an operation and everything else waits for it to finish, causing perceived lag (such as when you do a large WorldEdit operation).

    If a function takes a while to compute, the code that called it has switch execution over to the function until it returns. Copying and pasting the code out of the function would yield the same thing (though java.util.Math makes native calls so you can't really copy+paste).

    Twice per tick is not much on a modern computer, so you shouldn't worry about it until you actually see real problems.

    Actually, you could speed it up quite a bit if you are only working within a small domain (argument is greater than negative pi and less than pi, or around that ballpark). A five-term Taylor series about pi could suffice if the angle is between zero and 2 pi. I'm not sure how this compares to the actual performance of the sin() function (and i bet it's worse). You can also look around for some fast sine approximation formulas that run several times faster than the actual sine function.
  4. Offline


    coasterman10 is absolutly right. Since you have to use sinus and cosinus a damn lot on graphics programming, you don't have to worry, if you call those methods 1000 times. They are pretty fast.
    If you get in trouble because you calculate them very often, consider doing some tasks on an ExecutorService (aka. ThreadPool).
  5. Offline


    Just ran a test with 1000 sine operations on my desktop.
    (Intel Core i7-2600k, 4.3 GHz, Linux 3.13.0-24-generic, Oracle Java 7)

    Total time: 133531 nanoseconds = 0.133531 ms

    On my computer, you could run about 374,000 sine operations per Minecraft tick, or nearly 7.5 million per second.

    Now, you may not have 4.3 GHz servers running this, but even a 2 GHz server should see no more than a really tiny amount of time. When we talk about performance issues in Java, we usually mean when it gets into hundreds or thousands of iterations.

    You can run this test yourself with the code I used:
    1. long startTime = System.nanoTime();
    2. for (int i = 0; i < 1000; i++)
    3. Math.sin(i);
    4. long endTime = System.nanoTime();
    5. System.out.println("Time for 1000 sine operations: " + (endTime - startTime) + " ns");
  6. Offline


    Thread safety shouldn't be in question at all, since you're not using an async thread and doing anything involving concurrency. All that would happen is things in the game would pause momentarily, but that would only be if you were performing a ton of calculations that take a long time to do. As they've already said, you can perform thousands of trig calculations in one tick, provided you keep them relatively simple.

    coasterman10 Sorry if this is annoying, but I'm honestly astounded to see you mentioning a Taylor series; never thought I'd see that outside of calculus class. It makes sense, I'm just extremely surprised to see anyone mentioning it. A seven term taylor would essentially be a close enough approximation to doing the actual sin function, right? (Just want to be sure I'm theorizing this correctly).
  7. Offline


    kennethbgoodin I looked at some graphs and 7 terms is practical for fairly good approximation for pi away from its center, so centered around pi would get you from 0 to 2pi, outside of which you can just add/subtract 2pi as necessary to get back within the domain. Looking at my results though this many terms may be slower than the actual trig function, but this depends on platform since java.util.Math is mostly native method implementations. Time to go run another test.

    EDIT: Turns out I actually meant to the seventh power, or the 4th term. But the 5th term gives a better approximation at the zeros at pi and -pi so going to the 9th power would be better.
  8. Offline


    Microbenchmarking like this has a larger margin of error than the actual results, especially in Java. Since you never use the result of the method call for anything, HotSpot might skip the loop all-together after just a few iterations. It's clever like that. A thousand runs isn't a lot though, and the JIT compiler usually isn't invoked until after ~1500 runs for Java client or ~15k runs for Java server. At any rate, you are likely measuring the performance of context switches moreso than the performance of the sin() method.

    At the end of the day, premature optimization is the root of all evil in programming. Don't worry about optimizing stuff until you know that it is necessary. The trigonometry functions are not as fast as integer addition, but they are fast enough that you shouldn't worry about them unless they sit in a very, very tight inner loop somewhere (as would be the case for shaders in a graphics engine, where microoptimizations can mean the difference between 100 FPS and 30 FPS).

    As for the topic of thread-safety, the entire java.lang.Math class is thread-safe, but note that the random() method, for instance, is synchronized because it uses one instance of java.util.Random for all calls to the method (i.e. there is state in java.lang.Math), which means you shouldn't use that method if you have many threads trying to generate random numbers at the same time.
  9. Offline


    garbagemule After several tests I did notice a significant fluctuation in the reported time difference but the point was to demonstrate the ballpark in which the execution time actually lies (which is in the rank of nanoseconds, not entire milliseconds). I also confirmed sin() was actually being executed by replacing it with a semicolon and noticing that the execution time went down dramatically. I will agree though that premature micro-optimization is a waste and this demonstrates that it's completely okay to use standard functions if there isn't a real issue at hand.
Thread Status:
Not open for further replies.

Share This Page