在一台新电脑(酷睿i7-4820K 3.7 GHz,华硕P9X79 LE, GeForce GTX 650, Windows 8.1和Ubuntu 14.04)上运行Java绘图基准测试,在Java RTE 1.7.0_65的Windows上,使用vsync以60帧/秒左右的速度运行。然后,在后来的一个场合,基于其他pc的速度,我以400+ FPS的速度运行。现在又回到了60帧/秒。在最轻的测试中,CPU利用率几乎为0%,GPU为10%。一个新的图形驱动程序没有什么不同。同样的类文件在linux java 1.7.0_55下,在Ubuntu上可以得到400 FPS,在JDK 6和JDK 7下编译没有差别。
下面是没有加载、计算和显示图像函数的代码。在稍慢一点的Win 7电脑上,这款游戏的帧率接近600,但在新电脑上,帧率同样是60。通过HTML applet在线运行的代码变体以400+ FPS的速度运行。有人能解释一下吗?// Save as JavaDrawIt.java
// Compile command - javac JavaDrawIt.java
// Run command - java JavaDrawIt
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.Random;
public class JavaDrawIt extends JPanel implements ActionListener
{
Timer timer;
static int WIDTH = 1280 ;
static int HEIGHT = 720 ;
int gch = 1;
int gcm = 1;
int grn = 0;
String msg;
double fps;
double startTime;
double runTime = 0;
int frames = 0;
Random randNum = new Random();
private JavaDrawIt()
{
randNum.setSeed(999);
timer = new Timer(0, this);
startTime = (double)System.currentTimeMillis();
}
public void actionPerformed(ActionEvent e)
{
if (runTime <= 5.0)
{
repaint();
}
}
public void paintComponent(Graphics g)
{
int i;
float fh;
float fw;
super.paintComponent(g);
grn = grn + gch;
if (grn > 255)
{
gch = -gcm;
grn = 255;
}
if (grn < 1)
{
gch = gcm;
grn = 0;
}
g.setColor(new Color(0, grn, 255));
g.fillRect(0, 0, WIDTH, HEIGHT);
frames = frames + 1;
Graphics2D g2 = (Graphics2D)g;
Font font = new Font("MONOSPACE", Font.PLAIN, 24);
g2.setFont(font);
g.setColor (Color.WHITE);
runTime = ((double)System.currentTimeMillis() - startTime) / 1000;
fps = (double)frames / runTime;
msg = String.format(" %6.2f FPS, %6d Frames, %6.2f Seconds", fps, frames, runTime);
g2.drawString(msg, 10, 30);
}
public static void main(String[] args)
{
JFrame f = new JFrame(" JavaDrawIt");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JavaDrawIt m = new JavaDrawIt();
f.add(m);
f.setSize(WIDTH, HEIGHT);
f.setVisible(true);
m.timer.start();
}
}
我已经尝试了一些"线程安全"的例子,试图在新PC上获得更快的FPS速度,没有任何成功。最后一个只是刷新一个没有图形的空白屏幕。我注意到测量的FPS通常高达65 FPS,这表明它可能不是强制的垂直同步。这导致怀疑Swing Timer,我发现它可以校准,如下所示,还包括睡眠,等待,currentTimeMillis和nanoTime。
http://www.java2s.com/Code/Java/Swing-JFC/TimeResolution.htm下面产生Swing Timer测量:
// TimeResolution.java
// Copyright (c) 2007, Sun Microsystems, Inc
// All rights reserved.
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class TimeResolution implements ActionListener
{
private static int INCREMENT = 5;
private static int MAX = 50;
// Variables used in measurement of Swing timer
int timerIteration = 0;
int iterations = 0;
Timer timer;
long startTime, endTime;
int sleepTime;
public void actionPerformed(ActionEvent ae)
{
if (++timerIteration > iterations)
{
timer.stop();
timerIteration = 0;
endTime = System.nanoTime();
long totalTime = (endTime - startTime) / 1000000;
float calculatedDelayTime = totalTime / (float)iterations;
System.out.printf(" %2d %5d %5d %5.2fn",
sleepTime, iterations, totalTime, calculatedDelayTime);
}
}
public void measureTimer()
{
System.out.printf(" measuredn");
System.out.printf("timer delay iterations total time per-delayn");
for (sleepTime = 0; sleepTime <= 5; ++sleepTime)
{
iterations = (sleepTime == 0) ? 1000 : (1000 / sleepTime);
timerIteration = 1;
timer = new Timer(sleepTime, this);
startTime = System.nanoTime();
timer.start();
while (timerIteration > 0)
{
try
{
Thread.sleep(1000);
}
catch (Exception e)
{
}
}
}
}
// Execute the various timer resolution tests.
public static void main(String args[])
{
TimeResolution timeResolution = new TimeResolution();
timeResolution.measureTimer();
}
}
以下显示了PC产生快速FPS的结果和第二种来自新PC的结果。在休眠时间为零的情况下,另一台PC产生0.11毫秒,新一台产生15.66毫秒,相当于近64 FPS,前者的限制为9091 FPS。
有人知道是否有可能强制最小摆动计时器分辨率吗?
Other PC
timer delay iterations total time per-delay
0 1000 113 0.11
1 1000 15600 15.60
2 500 7800 15.60
3 333 5195 15.60
4 250 3900 15.60
5 200 3120 15.60
New PC
0 1000 15660 15.66
1 1000 15625 15.63
2 500 7812 15.62
3 333 5203 15.62
4 250 3906 15.62
5 200 3125 15.63
我相信这是一个完整的答案(前面低估了)。我希望这里有人知道,但事实并非如此。所以我不得不自己做研究。
在使用Javax swing timer = new timer (0, this)时,主活动的执行应该在指定actionPerformed之后以零睡眠时间重新开始。我找到了一个简单的程序来演示计时,没有图形活动,并将其修改为测量单个动作(帧)和累计帧每秒所花费的时间。结果如下:
几帧后,有问题的PC切换到15.3+ ms的常规睡眠时间,可能,正如这里的另一条消息所报道的那样,由Windows时间片粒度控制为1000 ms/64或15.625 ms,另一台PC表示开始时开销很高,但在50帧中记录了543 FPS。
New PC Win 8.1 Other PC Win 7
Frames FPS This frame FPS This frame
microsecs microsecs
1 500 2603 166 6170
2 44 42630 333 100
3 57 7051 500 107
4 58 15402 571 74
5 60 15550 555 231
6 60 15633 117 41586
7 60 15483 134 817
8 61 15396 106 22895
9 61 15388 107 8374
10 61 15370 117 867
11 62 15321 129 41
12 62 15418 141 217
13 62 15390 152 88
14 62 15353 162 73
To
45 63 15353 494 45
46 63 15360 505 48
47 63 14998 516 45
48 63 15306 521 113
49 63 15331 532 64
50 63 15365 543 44
Java程序如下。
// From http://www.java2s.com/Code/JavaAPI/javax.swing/Timerstop.htm
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.Timer;
class MainClass extends JFrame
{
Timer timer;
long startTime, startTimeF, runTime, fps, usecs;
int counter;
MainClass(String title)
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
ActionListener a = new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
runTime = (System.nanoTime() - startTime) / 1000000;
usecs = (System.nanoTime() - startTimeF) / 1000;
fps = counter * 1000 / runTime;
System.out.println(" Frames = " + counter + " " + fps + " FPS This frame " + usecs + " microseconds");
startTimeF = System.nanoTime();
if (++counter > 50)
{
timer.stop();
System.exit(0);
}
}
};
timer = new Timer(0, a);
startTime = System.nanoTime();
startTimeF = System.nanoTime();
counter = 1;
timer.start();
setSize(800, 600);
// pack();
setVisible(true);
}
public static void main(String[] args)
{
new MainClass("Timer Demo1");
}
}