What is fast, wait notify or busy wait in Java?

前端 未结 4 1807
一个人的身影
一个人的身影 2021-01-05 07:28

I understand that use of busy-wait is not a good programming practice and preferably synchronized object (wait-notify) should be used whenever possible. But I would like to

相关标签:
4条回答
  • 2021-01-05 07:45

    Depends. There are several scenarios:

    SCENARIO A

    If in the 'busy-wait' is waiting for is a hardware operation (for example, reading a sector from a hard disk to memory):

    1) The hardware will do the operation.

    2) The driver will launch an interruption.

    3) The operating will halting the actual process (your busy-wait process), saving the actual value any CPU registers the interruption will override in its procesing.

    4) The interruption will be processing, modifing any flags that indicates the data is available (in case of a disk reading).

    5) any overriden registers will be restored.

    6) Your process will continue with its process. Just in the next iteration it will call to the conthe condition of the loop. For example, if the busy wait is:

    while( !fileReady() ){
        ...
    }
    

    The fileReady() method will be a method that internally will check if a concrete flag (the flag that is modified in 4) is set or not. 7) So just in the next iteration, the loop will enter and do the opeartions.

    Take in mind that if there are another process running (operating system processes, other programs), they will put your process to the process tail. Also, the operating system can decide that, given your process has used all the CPU cycles he could (it spend its quantum), it will have less priority that other process that went to sleep (instead using a busy-wait approach) when it needed to wait for a certain condition.

    Conclusion. It is faster if no other external process are running (very unlikely) in the same core/CPU.


    SCENARIO B

    By the other hand, if the busy-method is waiting for another process to end (or set any variable to a certain value, the busy-wait will be slower.

    1) The busy-method will run in the CPU. Since the other process is not running, the conditions can't change, so the busy-method will run until the CPU ddecides to give CPU time to another process.

    2) The other process will run. If this process spend the time without achieve the confition the busy-wait process need, then go to 1), else continue to 3)

    3) The other (not the busy-wait) process will still run for a while, until the cpu decides a new process change.

    4) The busy-method will run again, but the conditions now is meet, so the operations ar now done.

    Conclusion: Its slower, and we also slow the entire process.


    SCENARIO C

    What if we have the same scenario than B, but with several cores (one process in each core)?.

    First, remember that even if you have a CPU with several cores, your program may is not allowed to use more than one. And maybe there are some operating system or other programs using them.

    Second, it thas not worth the price. Take in mind that the process must communicate to allow the busy-wait to find out that the condition is meet. This is usually can be done by a 'final' variable so you will need a enter in a synchronize block each time you evaluate the condition (you can't lock before entering the loop and no unlock it, beacause in taht case the other process will not be able to change the variable. So you will need something like this:

    boolean exit = false;
    while( exit==false ){
        synchronize(var){
            if(var = CONDITIONS_MEET)
                exit=true;
        }
    }
    //operations...
    

    }

    ¡¡ But the wait-notify will do somethig similar, and more efficiently (at language-level), without wasting CPU cycles, and using good prectices !!

    Conclusion: your are complicating your life doing somthing that very unlikely will be faster (very very very unlikely).


    Final conclusion: Only if you are in a very very simple scenario when you know concrete details of the operating system and the environment which your program will be run, you can consider a busy-wait approach.

    I hope this will anserw your question. Don't hesitase to ask if somthing is not clear.

    0 讨论(0)
  • 2021-01-05 07:52

    One is ready to sacrifice CPU cycles on busy waits as it is faster. An example on where busy wait is real time low latency application.

    There is a framework out there called lmax disruptor that was built for London Stock Exchange and one of the locking strategy is busy wait and that is how they use it.

    In order to be ultra-fast it is prefered to waste cpu cycles insted of waisting time while your lock is notified.

    You are right with all the other stuff and if you google a little bit on disruptor and read their papers, you will get more clarified. There are too many things to say on high performance and low latency.

    One good blog to look at is Mechanical Sympathy.

    0 讨论(0)
  • 2021-01-05 07:53

    Busy Wait is Faster than normal wait notify.

    1. But Why you wait? because one producer or other thread will do some work and then set a condition(or notify) so that you can actually come out of the busy/wait loop. Now suppose if your Producer is doing some heavy duty task then you actually eats its cpu cycles by doing busy wait (mainly true in single processor system), Which will in turn may make your system overall slow.

    2. So now when you should use busy wait. As Claudio says its mainly used in low latency system. But still its not be used blindly. Use busy wait when your producer is producing around a steady rate. If your producer produces items at variable rate (normally demonstrated by Poisson Distribution) then you should probably use wait notify.

    3. Normally the best tradeoff in high Throughput and Low latency systems is to go with do a busy wait for some time and then go to the wait(). If your system requires Ultra Lowlatency then you may go with many optimizations one of which could be Busy-Wait. But it should not be that every thread is doing busy-wait. Make sure only some Consumers may be around N/2 Consumers are doing busy wait where is N is the number of cores in your System. Wasting CPU cycles may effect overall performance and responsiveness of your system. For your ref: Even Normal ReentrantLock and its variants apply these strategies. i,e i.e when a thread calls lock.lock() its tries to acquire lock two times before qoing into the queue and wait for lock to be released. For Low Latency Systems you can even define your own lock for specific scenarios where they try for more 10 times before going into the queue (they will be variant of so called spin locks)

    0 讨论(0)
  • 2021-01-05 07:54

    Experimentation suggests that you will see the flag sooner if you busy wait than if you wait and notify (on my hardware, anyway). (Details below.) The difference is very very very very very small and so this would only be applicable to very rare apps. Stock trading apps, for instance, where companies are after any advantage they can get (vying to locate their servers as near the exchange as possible to get microsecond improvements in their network feeds from the exchange and such) might consider the difference worth it. I can imagine some science applications as well.

    In the vast majority of apps, the difference will be in effect no difference at all.

    But what happens to the CPU is, of course, that one of the cores hard-pegs:

    hardpeg

    That's bad in terms of impacting other processes on the box and in terms of power consumption in the data center.

    So: Use with extreme reluctance, only in situations where it really matters.


    Data (very small sample, but the code follows):

    Busy Wait:       10631  12350  15278
    Wait and Notify: 87299 120964 107204
    Delta:           76668 108614  91926
    

    Times are in nanoseconds. Billionths of a second. The average delta above is 92403ns (0.092402667 milliseconds, 0.000092403 seconds).

    BusyWait.java:

    public class BusyWait {
    
        private static class Shared {
            public long setAt;
            public long seenAt;
            public volatile boolean flag = false;
        }
    
        public static void main(String[] args) {
            final Shared shared = new Shared();
            Thread notifier = new Thread(new Runnable() {
                public void run() {
                    System.out.println("Running");
                    try {
                        Thread.sleep(500);
                        System.out.println("Setting flag");
                        shared.setAt = System.nanoTime();
                        shared.flag = true;
                    }
                    catch (Exception e) {
                    }
                }
            });
            notifier.start();
            while (!shared.flag) {
            }
            shared.seenAt = System.nanoTime();
            System.out.println("Delay between set and seen: " + (shared.seenAt - shared.setAt));
        }
    }
    

    WaitAndNotify.java:

    public class WaitAndNotify {
    
        private static class Shared {
            public long setAt;
            public long seenAt;
            public boolean flag = false;
        }
    
        public static void main(String[] args) {
            (new WaitAndNotify()).test();
        }
        private void test() {
            final Shared shared = new Shared();
            final WaitAndNotify instance = this;
            Thread notifier = new Thread(new Runnable() {
                public void run() {
                    System.out.println("Running");
                    try {
                        Thread.sleep(500);
                        System.out.println("Setting flag");
                        shared.setAt = System.nanoTime();
                        shared.flag = true;
                        synchronized (instance) {
                            instance.notify();
                        }
                    }
                    catch (Exception e) {
                    }
                }
            });
            notifier.start();
            while (!shared.flag) {
                try {
                    synchronized (this) {
                        wait();
                    }
                }
                catch (InterruptedException ie) {
                }
            }
            shared.seenAt = System.nanoTime();
            System.out.println("Delay between set and seen: " + (shared.seenAt - shared.setAt));
        }
    }
    
    0 讨论(0)
提交回复
热议问题