When you thought you could go to sleep for a nanosecond...



  • While reading through the Java 5.0 source code, to get a glimpse of how things are done, I find lots of WTFs. Mostly they are things like

    // This could be done a better way
    .. (some code)

    or the use of lots of deprecated methods. Those are supposed to only exist for backward compatibility of ancient source code and not for internal use.

    Today I found something different. This snippet of code comes directly from the source code file Thread.java.

    Pay close attention to the agreement of the comment and the actual executed sleep time.

        /**
         * Causes the currently executing thread to sleep (cease execution) for the
         * specified number of milliseconds plus the specified number of
         * nanoseconds. The thread does not lose ownership of any monitors.
         *
         * @param millis
         *            the length of time to sleep in milliseconds.
         * @param nanos
         *            0-999999 additional nanoseconds to sleep.
         * @exception IllegalArgumentException
         *                if the value of millis is negative or the value of nanos
         *                is not in the range 0-999999.
         * @exception InterruptedException
         *                if another thread has interrupted the current thread. The
         *                <i>interrupted status </i> of the current thread is
         *                cleared when this exception is thrown.
         * @see java.lang.Object#notify()
         */
        public static void sleep(long millis, int nanos) throws InterruptedException
        {
            if (millis < 0)
            {
                throw new IllegalArgumentException("timeout value is negative");
            }

            if (nanos < 0 || nanos > 999999)
            {
                throw new IllegalArgumentException("nanosecond timeout value out of range");
            }

            if (nanos >= 500000 || (nanos != 0 && millis == 0))
            {
                millis++;
            }

            sleep(millis);
        }



  • Well, obviously the contract currently promises more than Java resp. the OS can deliver, so they are going for the "closest match" possible (without busy waiting).



  • This has really bothered me alot too, not just the nano resolution, but the fact that sleeping 1millisec takes alot more than 1 millisec to execute.. I have tried this on many machines. On some machines Thread.sleep(1) takes 50ms, some 25ms and this one, with amd64 (wee fast) takes 16-17ms.. That's the best resolution I've ever seen for Thread.sleep(1).. So nanosecs.. haha.. not in a million years on at least the windows port. I understand it does differently on other platforms or if it's an app or applet etc.

    Going through the search for "sleep()" on sun bug DB gives pages of bugs on this.

    bug search

    Maybe it will all get fixed some day.. But if your java app/applet does some animation, sleeping less than 30ms and relying on that for making it look good will make the anim look horrible on alot of computers.

    </rant>
     



  • Sun has always had a big-complex. Just as Sun's ZFS file system, which needs the power of boiling all the oceans to fill a volume of maximum size. However sleeping could be improved if the JVM runs on a different platform, and don't forget processors that directly run native Java bytecode. I also don't understand why the nanoseconds has to be seperate. Almost as if waiting 3.5 milliseconds needs two operations, namely waiting 3 milliseconds and waiting 500000 nanoseconds. Having a single nanoseconds variable woudn't be a problem, as long as the datatype is 'long', which it already is. A long can store over 290 years of nanoseconds' worth.

    As I was saying, the comments (the javadoc) for this method makes one believe that nanoseconds actually matter. Believing that, I actually made a program that calculates the amount of nanoseconds that remain after an operation before it must continue, and I split them up in milliseconds and remainder using / and %. (Note: both those operators are show as hell, like 20 times slower than for example * or >>) If the javadoc for this sleep method had warned about how it is currently only accurate to milliseconds (and might be more accurate in the future), these practices can be prevented.
     



  • [quote user="peaveydk"]

    This has really bothered me alot too, not just the nano resolution, but the fact that sleeping 1millisec takes alot more than 1 millisec to execute.. I have tried this on many machines. On some machines Thread.sleep(1) takes 50ms, some 25ms and this one, with amd64 (wee fast) takes 16-17ms.. That's the best resolution I've ever seen for Thread.sleep(1).. So nanosecs.. haha.. not in a million years on at least the windows port. I understand it does differently on other platforms or if it's an app or applet etc.

    Going through the search for "sleep()" on sun bug DB gives pages of bugs on this.

    bug search

    Maybe it will all get fixed some day.. But if your java app/applet does some animation, sleeping less than 30ms and relying on that for making it look good will make the anim look horrible on alot of computers.

    </rant>
     

    [/quote]

    That's waaay to slow. Did you forget to subtract some overhead time or something? On my PC (AMD64 mobile with 32-bit version of Windows) it takes between 1.1and 3.8ms to sleep 1ms (depending on the moment of execution). Waiting more than 10ms really increases the accuracy too, with an average amount of 0.4ms too long. The code I used to proof this is:

    public class Test implements Runnable
    {
        public static void main(String[] args)
        {
            new Thread(new Test()).start();
        }

        public void run()
        {
            for (int i = 1; i <= 20; i++)
                try
                {
                    long delay = System.nanoTime();
                    Thread.sleep(i);
                    delay = System.nanoTime() - delay;
                    long overhead = System.nanoTime();
                    overhead = System.nanoTime() - overhead;
                    System.out.println("Waiting " + i + "ms takes " + (delay - overhead) / 1e6 + "ms");
                }
                catch (InterruptedException e)
                {
                }
        }
    }

    And did I mention I ran this on Windows *kuch*?



  • Zom-B: interesting, thx.. I did test this with the java plugin back when I dev'ed online casual games.. Now I ran your code (also on my win2k) with the *old* first edition amd64 3200+.. and I get totally random results every time

    >java Test
    Waiting 1ms takes 15.319545ms
    Waiting 2ms takes 14.030554ms
    Waiting 3ms takes 3.779811ms
    Waiting 4ms takes 4.7872ms
    Waiting 5ms takes 5.260445ms
    Waiting 6ms takes 6.751418ms
    Waiting 7ms takes 7.686172ms
    Waiting 8ms takes 8.693004ms
    Waiting 9ms takes 9.674134ms
    Waiting 10ms takes 29.700144ms
    Waiting 11ms takes 15.529628ms
    Waiting 12ms takes 0.368761ms
    Waiting 13ms takes 13.575747ms
    Waiting 14ms takes 14.559672ms
    Waiting 15ms takes 15.5327ms
    Waiting 16ms takes 16.510478ms
    Waiting 17ms takes 17.484903ms
    Waiting 18ms takes 18.460725ms
    Waiting 19ms takes 19.441577ms
    Waiting 20ms takes 39.460044ms
     

    notice the 3ms and 12ms sleep.. every run produces random results.. but funny enough, the 20ms sleep always takes 39ms 

     

    and on Linux I get 100% consistant timing:

    P4 Northwood 2Ghz single core, no HT

    Linux proto 2.6.17-1.2174_FC5 #1 Tue Aug 8 15:30:55 EDT 2006 i686 i686 i386 GNU/Linux

    # /opt/java/bin/java Test
    Waiting 1ms takes 7.795ms
    Waiting 2ms takes 7.897ms
    Waiting 3ms takes 7.682ms
    Waiting 4ms takes 7.744ms
    Waiting 5ms takes 11.749ms
    Waiting 6ms takes 11.758ms
    Waiting 7ms takes 11.911ms
    Waiting 8ms takes 11.576ms
    Waiting 9ms takes 15.748ms
    Waiting 10ms takes 15.59ms
    Waiting 11ms takes 15.748ms
    Waiting 12ms takes 15.744ms
    Waiting 13ms takes 19.738ms
    Waiting 14ms takes 19.76ms
    Waiting 15ms takes 19.742ms
    Waiting 16ms takes 19.53ms
    Waiting 17ms takes 23.742ms
    Waiting 18ms takes 23.647ms
    Waiting 19ms takes 23.675ms
    Waiting 20ms takes 23.367ms

    the deviation on each run is no more than 1ms as opposed to the random deviation on my win box..

    I used sun's javac and java version 1.6.0 beta2 on both machines

    Well the test code could have been better, but it illustrates the idea. But the WTF is that it is not given any mention in the javadoc, even though there are hundreds and hundres of bug reports on this issue.. Conclusion: if you wanna do something that really requires precise timing, dont use java and windows :)



  • You should ALWAYS consider sleep() as a "minimum" time.  The reason ofcourse is that your thread gives up is "rights" to the CPU, and the CPU scheduler doesn't have any obligation to run that thread.

    Just like "yield()" is typically a sleep for zero seconds, you might not get CPU controll back if a higher priority thread is eating CPU.

     



  • [quote user="danielpitts"]You should ALWAYS consider sleep() as a "minimum" time.  The reason ofcourse is that your thread gives up is "rights" to the CPU, and the CPU scheduler doesn't have any obligation to run that thread.

    Just like "yield()" is typically a sleep for zero seconds, you might not get CPU controll back if a higher priority thread is eating CPU.[/quote]

    But in a perfect world.. But yeah, I grok that, and I think both the OP and I agree that it is the javadoc that is to blame. Not only does it tell you can sleep nanoseconds and milllisecond while only being able to sleep millisecs, it also does not touch the subject of "we cannot guarantee".. If your only experience with programming is java and you have no understanding of your OS kernel and only read the javadoc, you will be grossly mislead :)

    I miss coding asm for C64, you could always count on the raster and work out the mips count to make sure you could multiplex that last sprite.. Multitasking has brought nothing but evil and dispair ;) 



  • [quote user="peaveydk"]

    I miss coding asm for C64, you could always count on the raster and work out the mips count to make sure you could multiplex that last sprite.. Multitasking has brought nothing but evil and dispair ;) 

    [/quote]

    Actually, (and I know you were kidding, but anyway) Multitasking is not inherently evil.

    The problem (if you can call it that) is the way that it is general implemented.  Using preemptive multitasking, many a common OS prevent a single "thread" from hogging the entire CPU. This is fine if you don't need real-time control at a specific granularity.  There are operating systems designed around cooperative multitasking (a thread doesn't lose the CPU unless it gives it up explicitly) in which you can get better guarranties about timing.

    There are probably other ways to get the same effect, but they involve special task switchers designed for that requirement.

         



  • [quote user="peaveydk"]

    Zom-B: interesting, thx.. I did test this with the java plugin back when I dev'ed online casual games.. Now I ran your code (also on my win2k) with the *old* first edition amd64 3200+.. and I get totally random results every time

    >java Test
    Waiting 1ms takes 15.319545ms
    Waiting 2ms takes 14.030554ms
    Waiting 3ms takes 3.779811ms
    Waiting 4ms takes 4.7872ms
    Waiting 5ms takes 5.260445ms
    Waiting 6ms takes 6.751418ms
    Waiting 7ms takes 7.686172ms
    Waiting 8ms takes 8.693004ms
    Waiting 9ms takes 9.674134ms
    Waiting 10ms takes 29.700144ms
    Waiting 11ms takes 15.529628ms
    Waiting 12ms takes 0.368761ms
    Waiting 13ms takes 13.575747ms
    Waiting 14ms takes 14.559672ms
    Waiting 15ms takes 15.5327ms
    Waiting 16ms takes 16.510478ms
    Waiting 17ms takes 17.484903ms
    Waiting 18ms takes 18.460725ms
    Waiting 19ms takes 19.441577ms
    Waiting 20ms takes 39.460044ms[/quote]

    Wtf?

    This is the output I have:

    Waiting 1ms takes 1.94969ms
    Waiting 2ms takes 2.698108ms
    Waiting 3ms takes 3.743492ms
    Waiting 4ms takes 4.948674ms
    Waiting 5ms takes 5.61468ms
    Waiting 6ms takes 6.750579ms
    Waiting 7ms takes 7.729195ms
    Waiting 8ms takes 8.707531ms
    Waiting 9ms takes 8.844141ms
    Waiting 10ms takes 10.641855ms
    Waiting 11ms takes 11.6607ms
    Waiting 12ms takes 12.589588ms
    Waiting 13ms takes 13.604522ms
    Waiting 14ms takes 19.837158ms
    Waiting 15ms takes 15.055545ms
    Waiting 16ms takes 16.51886ms
    Waiting 17ms takes 17.452777ms
    Waiting 18ms takes 18.483355ms
    Waiting 19ms takes 19.423977ms
    Waiting 20ms takes 20.451203ms

    Are you maybe using a lot of other applications on the background?
     



  • [quote user="Zom-B"]Are you maybe using a lot of other applications on the background? [/quote]

    I made it a point to make sure no big background apps was running (no firewalls, antivirus or other such useless crap). Also I had the taskman running and watched it, running smooth on 0% i run the test, the cpu meter does a small jump (2-3%) and back down to 0%.. The result, as my first tests. Random and confusing. I'm gonna study the JNI sourcecode which is now available to see how they implement the sleeping and threading.. Maybe there is a WTF in there for the win port?


Log in to reply