0

在一台新 PC(Core i7-4820K 3.7 GHz、Asus P9X79 LE、GeForce GTX 650、Windows 8.1 和 Ubuntu 14.04)上运行 Java 绘图基准测试似乎可以通过带有 Java RTE 1.7.0_65 的 Windows 以大约 60 FPS 的速度运行。然后,有一次,根据其他 PC 上的速度,以预期的 400+ FPS 运行。现在它又回到了 60 FPS。在最轻量级的测试中,CPU 利用率几乎为 0%,而 GPU 小于 10%。新的图形驱动程序没有任何区别。相同的类文件通过 Ubuntu 和 linux java 1.7.0_55 获得 400 FPS,并且使用 JDK 6 或 7 编译没有区别。

下面是没有加载、计算和显示图像的函数的代码。这在较旧的稍慢的 Win 7 PC 上获得了近 600 FPS,但在新 PC 上再次获得了 60 FPS。通过 HTML 小程序在线运行的代码变体以 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,这表明它可能不是强制 VSYNC。这导致怀疑 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.2f\n", 
                    sleepTime, iterations, totalTime, calculatedDelayTime);
        }
    }

    public void measureTimer() 
    {
        System.out.printf("                                  measured\n");
        System.out.printf("timer delay   iterations   total time   per-delay\n");
        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();
    }
}

以下显示了产生快速 FPS 的 PC 的结果和新 PC 的第二个结果。睡眠时间为零时,另一台 PC 产生 0.11 毫秒,新的 PC 产生 15.66 毫秒,相当于接近 64 FPS,之前的限制为 9091 FPS。

有谁知道是否可以强制最低 Swing Timer 分辨率?

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
4

1 回答 1

0

我相信这是一个完整的答案(之前被低估了)。我希望这里有人知道,但事实并非如此。所以我不得不自己做研究。

在使用 Javax swing timer = new Timer(0, this) 时,在指示 actionPerformed 之后,主活动执行应该以零睡眠时间重新开始。我找到了一个简单的程序来演示计时,没有图形活动,并对其进行了修改,以测量单个动作(帧)所花费的时间和每秒累积的帧数。结果如下。

几帧后,有问题的 PC 切换到 15.3+ 毫秒的常规睡眠时间,可能正如另一条消息中所报告的,由 1000 毫秒/64 或 15.625 毫秒的 Windows 时间片粒度控制,另一台 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");
  }
}
于 2014-08-07T21:46:01.053 回答