草庐IT

java - 游戏循环中最佳 sleep 时间计算的研究

coder 2023-05-19 原文

在编写动画和小游戏时,我开始了解 Thread.sleep(n); 我依靠这种方法告诉操作系统我的应用程序何时不需要任何 CPU,并使用它使我的程序以可预测的速度运行。

我的问题是 JRE 在不同的操作系统上使用不同的方法来实现此功能。在基于 UNIX(或受影响)的操作系统上,例如 Ubuntu 和 OS X,底层 JRE 实现使用功能良好且精确的系统将 CPU 时间分配给不同的应用程序,从而使我的 2D 游戏流畅且无延迟.但是,在 Windows 7 和较旧的 Microsoft 系统上,CPU 时间分布似乎有所不同,您通常会在给定的 sleep 量后恢复 CPU 时间,从目标 sleep 开始大约 1-2 毫秒。但是,您偶尔会获得额外 10-20 毫秒的 sleep 时间。发生这种情况时,这会导致我的游戏每隔几秒就会滞后一次。我注意到我在 Windows 上尝试过的大多数 Java 游戏都存在这个问题,Minecraft 就是一个明显的例子。

现在,我一直在 Internet 上四处寻找解决此问题的方法。我见过很多人只使用 Thread.yield(); 而不是 Thread.sleep(n);,它以当前使用的成本为代价完美运行无论您的游戏实际需要多少 CPU,CPU 内核都会满负荷运行。这不适合在笔记本电脑或高能耗工作站上玩游戏,而且在 Mac 和 Linux 系统上是不必要的权衡。

环顾四周,我发现了一种常用的纠正 sleep 时间不一致的方法,称为“spin-sleep”,您一次只订购 1 毫秒的 sleep 并使用 System.nanoTime(); 检查一致性。 方法,即使在 Microsoft 系统上也非常准确。这有助于解决正常的 1-2 毫秒的 sleep 不一致,但对于偶尔爆发的 +10-20 毫秒的 sleep 不一致无济于事,因为这通常会导致花费的时间比我的循环的一个周期应该花费的时间要多在一起。

经过大量查找,我发现了 Andy Malakov 的这篇神秘文章,它对改进我的循环非常有帮助:http://andy-malakov.blogspot.com/2010/06/alternative-to-threadsleep.html

根据他的文章我写了这个 sleep 方法:

// Variables for calculating optimal sleep time. In nanoseconds (1s = 10^-9ms).
private long timeBefore = 0L;
private long timeSleepEnd, timeLeft;

// The estimated game update rate.
private double timeUpdateRate;

// The time one game loop cycle should take in order to reach the max FPS.
private long timeLoop;

private void sleep() throws InterruptedException {

    // Skip first game loop cycle.
    if (timeBefore != 0L) {

        // Calculate optimal game loop sleep time.
        timeLeft = timeLoop - (System.nanoTime() - timeBefore);

        // If all necessary calculations took LESS time than given by the sleepTimeBuffer. Max update rate was reached.
        if (timeLeft > 0 && isUpdateRateLimited) {

            // Determine when to stop sleeping.
            timeSleepEnd = System.nanoTime() + timeLeft;

            // Sleep, yield or keep the thread busy until there is not time left to sleep.
            do {
                if (timeLeft > SLEEP_PRECISION) {
                    Thread.sleep(1); // Sleep for approximately 1 millisecond.
                }
                else if (timeLeft > SPIN_YIELD_PRECISION) {
                    Thread.yield(); // Yield the thread.
                }
                if (Thread.interrupted()) {
                    throw new InterruptedException();
            }
                timeLeft = timeSleepEnd - System.nanoTime();
            }
            while (timeLeft > 0);
        }
        // Save the calculated update rate.
        timeUpdateRate =  1000000000D / (double) (System.nanoTime() - timeBefore);
    }
    // Starting point for time measurement.
    timeBefore = System.nanoTime();
}

SLEEP_PRECISION 我通常设置为大约 2 ms,SPIN_YIELD_PRECISION 设置为大约 10 000 ns,以便在我的 Windows 7 机器上获得最佳性能。

经过大量的努力,这绝对是我能想到的最好的。所以,由于我仍然关心提高这种 sleep 方法的准确性,而且我对性能仍然不满意,所以我想呼吁所有的 Java 游戏黑客和动画师,为更好的解决方案提供建议。 window 平台。我可以在 Windows 上使用特定于平台的方式来使其更好吗?我不关心在我的应用程序中有一些特定于平台的代码,只要大部分代码是独立于操作系统的。

我还想知道是否有人知道 Microsoft 和 Oracle 正在制定更好的 Thread.sleep(n); 方法实现,或者 Oracle future 的改进计划是什么他们的环境是音乐软件和游戏等需要高计时精度的应用程序的基础吗?

感谢大家阅读我冗长的问题/文章。我希望有些人会发现我的研究有帮助!

最佳答案

您可以使用 cyclic timermutex 相关联.这是 IHMO 做你想做的最有效的方式。但是你应该考虑跳帧以防计算机滞后(你可以在计时器代码中使用另一个非阻塞互斥体来做到这一点。)

编辑:需要澄清的一些伪代码

定时器代码:

While(true):
  if acquireIfPossible(mutexSkipRender):
    release(mutexSkipRender)
    release(mutexRender)

sleep 代码:

acquire(mutexSkipRender)
acquire(mutexRender)
release(mutexSkipRender)

起始值:

mutexSkipRender = 1
mutexRender = 0

编辑:更正初始化值。

以下代码在 Windows 上运行良好(以 50fps 精确循环,精确到毫秒)

import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.Semaphore;


public class Main {
    public static void main(String[] args) throws InterruptedException {
        final Semaphore mutexRefresh = new Semaphore(0);
        final Semaphore mutexRefreshing = new Semaphore(1);
        int refresh = 0;

        Timer timRefresh = new Timer();
        timRefresh.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                if(mutexRefreshing.tryAcquire()) {
                    mutexRefreshing.release();
                    mutexRefresh.release();
                }
            }
        }, 0, 1000/50);

        // The timer is started and configured for 50fps
        Date startDate = new Date();
        while(true) { // Refreshing loop
            mutexRefresh.acquire();
            mutexRefreshing.acquire();

            // Refresh 
            refresh += 1;

            if(refresh % 50 == 0) {
                Date endDate = new Date();
                System.out.println(String.valueOf(50.0*1000/(endDate.getTime() - startDate.getTime())) + " fps.");
                startDate = new Date();
            }

            mutexRefreshing.release();
        }
    }
}

关于java - 游戏循环中最佳 sleep 时间计算的研究,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5274619/

有关java - 游戏循环中最佳 sleep 时间计算的研究的更多相关文章

  1. ruby-on-rails - 使用 Ruby on Rails 进行自动化测试 - 最佳实践 - 2

    很好奇,就使用ruby​​onrails自动化单元测试而言,你们正在做什么?您是否创建了一个脚本来在cron中运行rake作业并将结果邮寄给您?git中的预提交Hook?只是手动调用?我完全理解测试,但想知道在错误发生之前捕获错误的最佳实践是什么。让我们理所当然地认为测试本身是完美无缺的,并且可以正常工作。下一步是什么以确保他们在正确的时间将可能有害的结果传达给您? 最佳答案 不确定您到底想听什么,但是有几个级别的自动代码库控制:在处理某项功能时,您可以使用类似autotest的内容获得关于哪些有效,哪些无效的即时反馈。要确保您的提

  2. ruby - 解析 RDFa、微数据等的最佳方式是什么,使用统一的模式/词汇(例如 schema.org)存储和显示信息 - 2

    我主要使用Ruby来执行此操作,但到目前为止我的攻击计划如下:使用gemsrdf、rdf-rdfa和rdf-microdata或mida来解析给定任何URI的数据。我认为最好映射到像schema.org这样的统一模式,例如使用这个yaml文件,它试图描述数据词汇表和opengraph到schema.org之间的转换:#SchemaXtoschema.orgconversion#data-vocabularyDV:name:namestreet-address:streetAddressregion:addressRegionlocality:addressLocalityphoto:i

  3. ruby-on-rails - 使用一系列等级计算字母等级 - 2

    这里是Ruby新手。完成一些练习后碰壁了。练习:计算一系列成绩的字母等级创建一个方法get_grade来接受测试分数数组。数组中的每个分数应介于0和100之间,其中100是最大分数。计算平均分并将字母等级作为字符串返回,即“A”、“B”、“C”、“D”、“E”或“F”。我一直返回错误:avg.rb:1:syntaxerror,unexpectedtLBRACK,expecting')'defget_grade([100,90,80])^avg.rb:1:syntaxerror,unexpected')',expecting$end这是我目前所拥有的。我想坚持使用下面的方法或.join,

  4. java - 等价于 Java 中的 Ruby Hash - 2

    我真的很习惯使用Ruby编写以下代码:my_hash={}my_hash['test']=1Java中对应的数据结构是什么? 最佳答案 HashMapmap=newHashMap();map.put("test",1);我假设? 关于java-等价于Java中的RubyHash,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/22737685/

  5. ruby-on-rails - Ruby 检查日期时间是否为 iso8601 并保存 - 2

    我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby​​是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查

  6. ruby-on-rails - 将 Ruby 中的日期/时间格式化为 YYYY-MM-DD HH :MM:SS - 2

    这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build

  7. ruby - 查找字符串中的内容类型(数字、日期、时间、字符串等) - 2

    我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s

  8. java - 从 JRuby 调用 Java 类的问题 - 2

    我正在尝试使用boilerpipe来自JRuby。我看过guide从JRuby调用Java,并成功地将它与另一个Java包一起使用,但无法弄清楚为什么同样的东西不能用于boilerpipe。我正在尝试基本上从JRuby中执行与此Java等效的操作:URLurl=newURL("http://www.example.com/some-location/index.html");Stringtext=ArticleExtractor.INSTANCE.getText(url);在JRuby中试过这个:require'java'url=java.net.URL.new("http://www

  9. java - 我的模型类或其他类中应该有逻辑吗 - 2

    我只想对我一直在思考的这个问题有其他意见,例如我有classuser_controller和classuserclassUserattr_accessor:name,:usernameendclassUserController//dosomethingaboutanythingaboutusersend问题是我的User类中是否应该有逻辑user=User.newuser.do_something(user1)oritshouldbeuser_controller=UserController.newuser_controller.do_something(user1,user2)我

  10. java - 什么相当于 ruby​​ 的 rack 或 python 的 Java wsgi? - 2

    什么是ruby​​的rack或python的Java的wsgi?还有一个路由库。 最佳答案 来自Python标准PEP333:Bycontrast,althoughJavahasjustasmanywebapplicationframeworksavailable,Java's"servlet"APImakesitpossibleforapplicationswrittenwithanyJavawebapplicationframeworktoruninanywebserverthatsupportstheservletAPI.ht

随机推荐