使用 LibGdx 在 Java 中奇怪的 CPU 使用率

Posted

技术标签:

【中文标题】使用 LibGdx 在 Java 中奇怪的 CPU 使用率【英文标题】:Strange CPU usage in Java using LibGdx 【发布时间】:2015-03-27 18:27:23 【问题描述】:

我目前正在使用 libgdx 框架开发一个小型桌面 java 游戏。一切都很好,除了一些非常奇怪的 CPU 使用行为。

当只运行带有空渲染循环的框架背景时,我得到大约 28% 的 CPU 使用率。考虑到 lbgdx 也在做一些管理并且尽可能快地调用渲染方法,这似乎很好。

但这里的事情开始变得疯狂......

当绘制和更新大约 200 个对象时,CPU 使用率会上升到大约 55%。然而,Libgdx 告诉我它只使用一个 open gl 绘图调用。那么 CPU 开销应该非常低,不是吗? CPU使用率也不是由更新循环引起的。我已经尝试删除它,但使用率仍然高达 50% 左右。

但它变得更糟......

我偶然发现,当程序必须处理大量对象(大约 50000 个)时,使用率下降到大约 30%。

如果我随后释放大量对象并返回到我的 200 个对象,CPU 使用率会进一步下降到 ~20%。

如果我重复这个过程,使用量最终会下降到 3%。是的 3%。 它将保持在 3%。 (只要我不分配任何其他对象。当然,使用量会增加)

总结一下:我正在渲染完全相同的场景。起初 CPU 使用率为 55%。在分配和释放大量对象后,CPU 使用率为 3%。

这怎么可能发生?

起初我认为lidgdx 使用的是批处理系统,它将创建一批顶点、颜色信息和纹理坐标,以最大限度地减少绘制调用的数量。 如果我将批量大小设置为每批 2500 个精灵,CPU 使用率将下降到 30% - 40%。然而,奇怪的分配和释放效应仍在发生。

如果有帮助,我还会提供我的更新、绘制、渲染和清除方法。

渲染方法:

@Override
public void render()
   
    //the basic game loop components
    //update all and draw everything
    update();
    draw();

    //Entity.alive refers to an ArrayList containing
    //all objects that'll be updated and draw in the
    //render loop
    System.out.println("ENTITY COUNT " + Entity.alive.size());

    //and remove all the dead bodies
    //from the carpet ... you know
    //i can't stand blood ...
    //and make some new ones, too 
    clear();

    while((System.nanoTime() / 1000000) - time_last < 16)
    
        //nothing to do here ... (imagine meme)

        try
        
            Thread.sleep(0, 1000);
        
        catch (InterruptedException e)
        
            e.printStackTrace();
        
    

    time_last = System.nanoTime() / 1000000;

更新方法:

public void update()
   
    //update all the entities
    //in the "alive" ArrayList
    for(Entity e: Entity.alive) e.update();

    //add all entities declared
    //dead to the dead list
    for(Entity e: Entity.alive)
    
        //not dead - nothing to do here
        if(e.getState() == true) continue;

        //else add to dead list!
        Entity.dead.add(e);
    

绘制方法:

public void draw()
   
    //clear the screen
    Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT | GL20.GL_DEPTH_BUFFER_BIT);

    //update the view
    Draw.updateCamera();

    //start a batch section
    //all further calls will
    //be packed an sent to
    //gpu in one flush
    Draw.startBatch();

    //draw all living entities
    for(Entity e: Entity.alive) e.draw();

    //draw all the level
    //geometry and sprites
    Level.draw();       

    //ending the sprite batch
    Draw.endBatch();

清除方法:

public void clear()

    //remove all dead objects
    for(Entity e: Entity.dead)
               
        Entity.alive.remove(e);
    

    //clear the array of the dead
    Entity.dead.clear();

    //add all the newly created
    //objects to the alive list
    for(Entity e: Entity.born)
    
        Entity.alive.add(e);
    

    //clear the creation list
    Entity.born.clear();

我正在使用 java 1.8.0_31-b13 在 eclipse 4.4.1 中开发程序。 操作系统为 Windows 8.1。

【问题讨论】:

你如何测量 CPU 使用率? CPU 使用率是否与您的帧率相关? 标记为此代码不起作用的人需要重新阅读标记说明。这是一个完全有效的问题,虽然很长而且有点罗嗦。 对 OP 也是如此:这完全是对编码风格的评论,但是您有点过分评论了。 Entity.dead.clear() 非常简单,并不值得评论。它增加了很多模糊,否则应该用于 cmets 解释不那么微不足道的部分。只是一个想法:) 感谢您的辩护! :) 我知道有点罗嗦,但很难用几句话来描述这个问题。关于评论:你是绝对正确的。过度注释代码的倾向只是我在大学时养成的习惯。因为很多不同的人会阅读你的代码,而分数有时取决于那些人对程序的理解,所以为了安全起见,我倾向于评论每一点。 【参考方案1】:

你的 render() 方法有点奇怪。 我不知道这是否会导致疯狂的 cpu 计时以下代码是不必要的,因为 LibGDX 本身处理 16.6ms 等待时间(对于 60Hz)本身。如果您的计时器与 LibGDX 计时器不匹配,可能会造成一些麻烦。因此,您可能会等待太久,从而显着降低 CPU 负载。删除实体时,计时器将保持不同步 -> 减少 CPU 使用率。

while((System.nanoTime() / 1000000) - time_last < 16)

    //nothing to do here ... (imagine meme)

    try
    
        Thread.sleep(0, 1000);
    
    catch (InterruptedException e)
    
        e.printStackTrace();
    

【讨论】:

首先:感谢您的回答! :) 你对 LibGdx 在后台所做的时间是正确的。所以我的计时码是多余的。但是,当我删除代码块时,使用量下降仍然存在。所以这似乎不是主要问题。但我可以想象内部 libgdx 计时器不知何故被大量对象的创建弄糊涂了。【参考方案2】:

好的,经过大量阅读和一些实验,我想通了!

在 libGDX 使用的 DesktopLauncher 类中,程序员被要求为 lwjgl 初始化进程创建一个配置对象。像这样的:

public static void main(String[] args)

    LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();

    config.width = 1920;
    config.height = 1080;
    config.fullscreen = true;
    config.foregroundFPS = 0;

    new LwjglApplication(new Controller(), config);

事实证明,最后一行 config.foregroundFPS = 0; 至关重要。 这将禁用框架尽可能将线程设置为睡眠的所有尝试。

我强烈怀疑这种睡眠功能会导致奇怪的 CPU 使用率。禁用此功能后,现在的使用行为正常。

仍然非常感谢您的支持! :)

【讨论】:

您能否接受您的回答,以便更明显地找到解决方案?谢谢【参考方案3】:

正如我所发现的,这个问题与 vSync 和一些特定的(nVidia?)驱动程序有关。您可以使用以下代码来防止 CPU 使用率过高:

LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.vSyncEnabled = false;

除 0 和 60 以外的任何 FPS 都可以:

LwjglApplicationConfiguration config = new LwjglApplicationConfiguration();
config.backgroundFPS = 59;
config.foregroundFPS = 59;

【讨论】:

以上是关于使用 LibGdx 在 Java 中奇怪的 CPU 使用率的主要内容,如果未能解决你的问题,请参考以下文章

列表理解中奇怪的 lambda 行为

Firefox 中奇怪的表格渲染

Swift 中奇怪的 UInt64 行为

wav文件中奇怪的滴答声

iOS6 中奇怪的 SplitView/NavigationController 行为

BMP文件中奇怪的十六进制数字