【问题标题】:Throttle CPU usage on background thread限制后台线程上的 CPU 使用率
【发布时间】:2015-06-24 16:43:22
【问题描述】:

我有一个 CPU 密集型任务,我希望它使用更少的 CPU 并占用更多时间。

我在启动时将大量 SCNNode 加载到场景中。它会占用大量内存,我希望它能够以安全的速度运行,而不是拖慢我的系统或可能导致系统崩溃。

这是我用来加载节点的代码。

dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^(void){
    NSLog(@"Start Loading Level");
    SCNNode *cameraNode = [SCNNode node];
    cameraNode.camera = [SCNCamera camera];
    cameraNode.camera.zFar = 5000;
    cameraNode.position = SCNVector3Make(0, 3000, 0);
    cameraNode.rotation = SCNVector4Make(1, 0, 0, -M_PI_2);
    [scene.rootNode addChildNode:cameraNode];
    for (int i = 0; i < 100000; i++)
    {
        int side = 3000;
        SCNNode *node = [SCNNode node];
        node.geometry = [SCNBox boxWithWidth:1 height:1 length:1 chamferRadius:0];
        node.position = SCNVector3Make(arc4random_uniform(side) - side / 2.0,
                                       0,
                                       arc4random_uniform(side) - side / 2.0);
        [scene.rootNode addChildNode:node];
    }
    dispatch_async(dispatch_get_main_queue(), ^(void){
        NSLog(@"Finished");
    });
});

以下是统计数据:

【问题讨论】:

  • SceneKit 必须渲染这么多节点,我会更加专注。 100000 个节点非常庞大。
  • 这是一个测试用例。我可以使用我自己项目中的代码,但这似乎明白了这一点。
  • 我建议在 dispatch_async() 中使用带有 stride 的 dispatch_apply() 而不是 for(;;) 来同时创建节点。
  • 感谢您的提示,但在 cpu 使用方面根本没有帮助。
  • 你确定它不会在你有一些竞争任务时自动使用正确的时间吗?如果是这样,您可以尝试定期调用 usleep() 或 sched_yield,但不确定它们如何与 GCD 交互。

标签: objective-c scenekit


【解决方案1】:

这可能不是最好的方法,但你看过prepareobject:吗?

创建 NSObjects 本身应该不是什么大问题,但准备几何图形并将其添加到场景中可能就是这样。将此步骤推迟到辅助线程可以节省一些 CPU 并使场景在节点准备好添加之前不会停止。

一旦每个节点准备就绪,(完成处理程序)只需将其添加到场景中。我不会限制加载,但它至少会以更安全的方式处理您应用的其余部分。

https://developer.apple.com/library/prerelease/ios/documentation/SceneKit/Reference/SCNSceneRenderer_Protocol/index.html#//apple_ref/occ/intfm/SCNSceneRenderer/prepareObjects:withCompletionHandler:

-(void)prepareLevel{
    // Mutable Array to save the nodes
    NSMutableArray *nodes = [[NSMutableArray alloc] init];

    for (int i = 0; i < 100000; i++)
    {
        int side = 3000;
        SCNNode *node = [SCNNode node];
        node.geometry = [SCNBox boxWithWidth:1 height:1 
                                      length:1 chamferRadius:0];
        node.position = SCNVector3Make(arc4random_uniform(side) - side / 2.0,
                                       0,
                                       arc4random_uniform(side) - side / 2.0);

        [nodes addObject:node];
    }

    [mySCNView prepareObjects:nodes withCompletionHandler:^(BOOL success) {
        for(SCNNode*node in nodes){
            [scene.rootNode addChildNode:node];
        }
    }];

}

【讨论】:

  • 这确实解决了我的问题,谢谢。你能用一段示例代码编辑你的答案吗?
  • 当然,我回家后会这样做。
  • 我添加了一个简单的代码示例。仍有优化空间,但展示了prepareObject:的基本用法
  • 是的!非常感谢。我对稍后会查看问题的人提出了更多要求,因为示例代码对这些类型的问题非常有帮助。
【解决方案2】:

两个想法:

  1. 使用 NSOperationQueue,它有方法 setThreadPriority: 可用于在一定程度上限制负载。

  2. 因为您在异步队列上进行调度,所以您可以在处理每 100 个左右的元素后插入一个 sleep() 函数。这应该会减少处理器的工作量,因为线程会在一定百分比的时间内停止,但不会让您的 UI 滞后,因为它只是一个后台队列。

显然这会牺牲 CPU 负载来换取执行时间。

【讨论】:

    【解决方案3】:

    您似乎没有采取任何措施来限制同时操作的数量。这样做可能比尝试限制 CPU 使用率更有效,因为相比之下,CPU 使用率限制是一个模糊的概念。

    要限制并发操作的数量,您可以执行以下操作之一:

    1. 使用dispatch_queue_create("com.myApp.loadQueue", DISPATCH_QUEUE_SERIAL) 自定义dispatch_queueDISPATCH_QUEUE_SERIAL 防止队列中的块并发运行。

    2. 正如@JoJoe 在他们的回答中提到的,使用NSOperationQueue。我建议使用setConcurrentOperationCount: 而不是setThreadPriority: 来限制并发操作的数量。如果您不想费心设置自己的NSOperation 子类,可以使用NSBlockOperation

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-02-26
      • 1970-01-01
      • 1970-01-01
      • 2011-06-22
      • 1970-01-01
      • 1970-01-01
      • 2018-08-02
      相关资源
      最近更新 更多