• Post Reply Bookmark Topic Watch Topic
  • New Topic

Graphics.drawImage Runs Erratically  RSS feed

 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Maybe this belongs in the Swing forum, but I'm not sure the issue I'm going to ask about is specific to Swing.

My code creates a JFrame that contains a subclass of a JPanel. It provides a BufferedImage for the JPanel to use when repainting itself, and then enters a loop where, every 33 milliseconds, it asks the JPanel to repaint itself. Inside the subclass of the JPanel's code, it uses System.nanoTime() to time how long the call to drawImage takes when the JPanel is repainted.

Here's the SSCCE:



I've divided the time consumed by drawImage() by 1000, just so I get it in microseconds, instead of nanoseconds. Here's some of the output:

776
818
1044
744
885
253
253
307
265
837
826
737
969
266
276
264
735
900
712
1028
767
955
778
223
254
909
724
273
874
719
843
730
770
696
269
255
817
738
743
750
778
255
1013
735
739
834
754


As you can see, it's fast, but it's rather erratic, varying from as little as 250 uS to over 1000 uS. Why should that be? I know nanoTime() is not all that accurate, but there's more to this that I can get into later, if that's the only reason at this level (preview: when I do things that slow drawImage() down for understandable reasons, it continues to be erratic, varying in speed by several milliseconds per call).

I would have expected more consistent performance. Anyone know why it's behaving this way?
 
Rob Camick
Ranch Hand
Posts: 2763
12
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Don't override paint() that is how you do custom painting in AWT, not Swing.

Custom painting is done by overriding paintComponent(). Also, don't forget to invoke super.paintComponent(). By not invoking super... you may lose the advantage of the default double buffering in Swing.

Don't know if this will change the timings, but this is the way painting should be done to follow the painting conventions of Swing.
 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Rob, for what I'm doing, those things just add overhead that doesn't buy me anything. The SSCCE above has nothing to do with my application, but my application simply paints the whole JPanel, with no other components and no unused space, over and over. It's a video window. Pretty much every time I put up a code fragment that directly overrides paint and doesn't call any other related methods, one or more people remind me that's not good practice. But, I've stepped into the code and looked at this from every angle. For dumb video, this works.

I believe the erratic timing on drawImage actually doesn't depend on the Graphics object being associated with a device. I'll rewrite my SSCCE and, if it confirms this, I'll post that next, so the issue of how to use a JPanel will just become moot.
 
Norm Radder
Rancher
Posts: 1773
26
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
varying from as little as 250 uS to over 1000 uS.

How are you keeping other OS programs from using the processor so that the timings are only for your code and don't include other OS stuff?
 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Here's a much simplified version, with no Swing in it:



Here are some of the reported execution times:

1101
1202
5033
1053
5601
7183
1005
1004
4379
5149
4447
4219
1116
4638
10603
1043
5962
6090
4661
4318
4445
4966
4414
3276
1017
5097
5399
5404
5632
4208
6250
1115
4279
4780
1000
1090
5667
4390
5017
6951
1100


Considerably slower than when copying to the screen, but that's not the question. The question still is: why does the time for this call vary so much?
 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Norm Radder wrote:
varying from as little as 250 uS to over 1000 uS.

How are you keeping other OS programs from using the processor so that the timings are only for your code and don't include other OS stuff?

I'm not, but if you replace the Thread.sleep with a long loop, and time that as well, a number of curious things happen:

1. The drawImage call speeds up significantly.
2. The time spent in drawImage becomes much more consistent.
3. The time spent in the loop is comparable to the drawImage time and shows about the same consistency.



774 - 844
772 - 850
806 - 849
770 - 845
949 - 861
771 - 844
771 - 847
795 - 844
774 - 844
787 - 875
787 - 841
771 - 848
770 - 839
771 - 840
771 - 844
770 - 839
771 - 840
773 - 882
786 - 843
771 - 841
883 - 847
785 - 847
771 - 848
771 - 848
770 - 840
771 - 867
815 - 841
772 - 841
771 - 842
771 - 839
789 - 851
780 - 850
770 - 839
773 - 867
799 - 845
771 - 847
771 - 841
771 - 841
784 - 854
773 - 841
772 - 873
772 - 840
786 - 841
785 - 839
771 - 843
809 - 871
789 - 922
789 - 864


Why would replacing the Thread.sleep call with a delay loop make drawImage either faster or less erratic?
 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This is really puzzling. Here's a frequency histogram of the time drawImage needs to run when called identically, but when each call is in a loop alternating with either a call to Thread.sleep, or else with a delay loop:



This shows that using Thread.sleep causes drawImage to, for the most part, take four times as long to run as when Thread.sleep is replaced with a delay loop, but that the run time for drawImage becomes bimodal when using Thread.sleep, with one mode being very near to the typical run time with a delay loop.

I strongly suspect I'm not measuring what I think I'm measuring, but this all started when I noticed some visible jitter in what should have been a smooth animation on my screen. Looking into my code revealed that drawImage was using very inconsistent times to do the exact same things, and that was where the jitter came from. Now I've reduced it to this code, but it reveals this very perplexing behavior (and I still don't know what causes the jitter in drawImage's execution time).

Anyone have an idea for further investigation?
 
Norm Radder
Rancher
Posts: 1773
26
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
A consideration: with the delay loop, the code doesn't give up the processor. With sleep it might allow the processor to be assigned to another task.
 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Norm Radder wrote:A consideration: with the delay loop, the code doesn't give up the processor. With sleep it might allow the processor to be assigned to another task.

Good point. I was pondering something along those lines, wondering if there might be some side-effect of the context switch that reactivates my program when the sleep expires, that steals cycles from the same process that has just awakened. Something along those lines might explain the bimodal nature of the blue histogram. The left peak might be reflective of the processor not having been reassigned.

I'll recode to replace the drawImage call with something of my own, so I can be sure I know what's executing, then see if the same erratic timing and strange effect of using Thread.sleep are still detectable.

Stay tuned...
 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Looks like you are on the right track, Norm. I replaced the call to drawImage with a call to a method that just takes a long time to run. Here's the program with that code replacing drawImage, and with the call to Thread.sleep:



And here is the same code, with the Thread.sleep call replaced by a delay loop:



And here's the histogram:



There appears definitely to be something about waking up from Thread.sleep that has an erratic delaying effect on the code that follows it. And it's not a delay in getting to that code, as that would also delay the assignment of System.nanoTime() to t0.

This is a real puzzler. Is there an alternative to Thread.sleep I could try?
 
Rob Camick
Ranch Hand
Posts: 2763
12
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Is there an alternative to Thread.sleep I could try?


I use a Timer when I want animation.

There is both a Swing Timer and an AWT Timer depending on your requirement.
 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Rob Camick wrote:I use a Timer when I want animation.


Thanks! I tried the Swing Timer. The bimodal runtime still shows up. Interestingly, if I run the delay loop on each startup of the ActionListener, the timed code returns to running consistently quickly. Whatever lingering effect of returning from a sleeping condition I am seeing doesn't last for 33 milliseconds.

I'm going to try some other tricks and, if I learn anything useful, I'll post. At this point, it's clear that the issue has nothing to do with Graphics.drawImage, so I'll probably start a new topic and merge this one with it, if I come up with anything.

Appreciate the help.
 
Carey Brown
Bartender
Posts: 2608
40
Eclipse IDE Firefox Browser Java MySQL Database VI Editor Windows
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I tried rolling my own using the nano version of sleep(). It seems to me that you want the rendering to start on multiples of 33 millis but you are less concerned about the actual render time as long as it doesn't interfere with the goal of 33 millis. Here is my code. It sort of works. And by that I mean if you sum up the delay on alternate renders they total almost exactly 33 millis (which my experiment with swing.Timer didn't even come close). So, I've been staring at this code for a while now trying to figure out why it toggles delay time. Maybe you can see something that I'm missing.


 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Carey, thanks for chiming in. I'll look at your code more closely, to see if I can answer your question. Regarding the nano version of sleep, it's kind of a fraud. Here's the source from the JDK:

As you can see, it simply rounds the nano part up or down to the nearest whole millisecond greater than zero, and calls sleep. Disappointing, isn't it?
 
Carey Brown
Bartender
Posts: 2608
40
Eclipse IDE Firefox Browser Java MySQL Database VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Very disappointing. My experiment (aside from the toggling issue) does show that you can achieve a higher loop time accuracy doing this as opposed to either the swing Timer or the util Timer.
 
Campbell Ritchie
Sheriff
Posts: 54078
130
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
The Object#wait method with the two parameters does the same thing.
 
Stevens Miller
Bartender
Posts: 1444
30
C++ Java Netbeans IDE Windows
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Campbell Ritchie wrote:The Object#wait method with the two parameters does the same thing.

Makes sense. As far as I can tell, pretty much all these things ultimately rely on the host OS's equivalent of "sleep." Amazingly, there is much discussion about sleeping on the internet that reflects both a mix of ideas about how it works, and some persistent misconceptions.

At this point, here's what I (think I) know:

1. At one time, and, possibly, still today, Windows boots with a timer granularity of 1/64th of a second.

2. Any application can request a finer granularity, to the machine-specific limit, which is almost always 1/1000th of a second.

3. Damned near every machine ends up running at a timer granularity of 1/1000th of a second, maybe thanks to Chrome, maybe just because.

4. Any timer of whatever kind that purports to respect sub-millisecond accuracy is lying to you, unless it is spinning.

5. When your application sleeps, it probably means your CPU speed drops to your power plan's minimum, and it can take up to 40ms to come back up to full speed when your sleep unblocks.

Amazingly, this all means you can get better performance from an application by spinning instead of sleeping, because your spinning delays can respect sub-millisecond accuracy (thus, you don't have to wait to the end of the next full millisecond to complete a delay you'd like to complete sooner), your spinning delays don't let the processor throttle down to a low CPU speed, and your spinning delays can be run either alternately on a thread that doesn't spin when the delay is completed, or on a thread with lower priority than your actual working thread (which means it doesn't spin when that would compete with something useful).

In my tests, I was initially suprised to see that, when I used spinning, my CPU consumption was only 13%. You would think that, when spinning alternates with some other compute-bound activity, that would be 100%. But... my computer (a Dell XPS-8700) has eight cores. A single-threaded spin, alternating with compute-bound activity on the same thread, will completely consume one of those eight cores. What's one-eighth of 100%? Yup, it's 12.5%, about 13%, to the nearest whole number.

Thus, my entire set of problems (consistency, accuracy, latency) are solved by using a dumb, almost universally reviled technique: just loop until the counter says it's time to stop looping.

Who would have seen that coming?
 
It is sorta covered in the JavaRanch Style Guide.
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!