Дроссельная загрузка ЦП в фоновом потоке

У меня есть задача с интенсивным использованием ЦП, и я хочу, чтобы она использовала меньше ЦП и занимала больше времени.

Я загружаю огромное количество SCNNodes в сцену при запуске. Он занимает много памяти, и я хотел бы, чтобы он работал с ним с безопасной скоростью, вместо того, чтобы отставать от моей системы или потенциально вызывать ее сбой.

Вот код, который я использую для загрузки узлов.

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");
    });
});

Вот статистика:

Использование изображения с течением времениПамять изображений


person Konrad Wright    schedule 17.04.2015    source источник
comment
Меня бы больше беспокоил тот факт, что SceneKit должен будет отображать так много узлов. 100000 узлов — это невероятно много.   -  person mnuages    schedule 17.04.2015
comment
Это тестовый случай. Я мог бы использовать код из своего собственного проекта, но это, кажется, понятно.   -  person Konrad Wright    schedule 17.04.2015
comment
Я бы предложил использовать dispatch_apply() с шагом вместо for(;;) внутри dispatch_async() для одновременного создания узлов.   -  person Ef Dot    schedule 18.04.2015
comment
Спасибо за совет, но он совершенно не помогает, когда дело доходит до использования процессора.   -  person Konrad Wright    schedule 20.04.2015
comment
Вы уверены, что он не будет автоматически использовать нужное количество меньшего времени, как только у вас появятся конкурирующие задачи? Если это так, вы можете попробовать периодически вызывать usleep() или sched_yield, но не знаете, как они взаимодействуют с GCD.   -  person user1055568    schedule 20.04.2015
comment
Что ж, при наличии достаточного количества кубов он израсходует достаточно памяти и приведет к сбою приложения. Я пытаюсь найти, как загрузить предустановленный уровень в SceneKit в фоновом режиме, не тормозя систему.   -  person Konrad Wright    schedule 20.04.2015


Ответы (3)


Возможно, это не лучший способ сделать это, но вы смотрели на prepareobject:?

Создание самих NSObjects не должно быть большой проблемой, но подготовка геометрии и добавление ее в сцену, вероятно, будет проблемой. Но откладывая этот шаг на вторичный поток, вы сэкономите процессор и заставите сцену не останавливаться, пока узел не будет готов к добавлению.

Как только каждый узел будет готов (обработчик завершения), просто добавьте его на сцену. Я не буду ограничивать загрузку, но, по крайней мере, она будет более безопасной для остальной части вашего приложения.

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];
        }
    }];

}
person Moustach    schedule 20.04.2015
comment
Это решило мою проблему, спасибо. Не могли бы вы отредактировать свой ответ с помощью примера кода? - person Konrad Wright; 27.04.2015
comment
Конечно, я сделаю это, когда вернусь домой. - person Moustach; 27.04.2015
comment
Я добавил простой пример кода. Еще есть место для оптимизации, но он показывает основное использование prepareObject: - person Moustach; 27.04.2015
comment
Да! Большое спасибо. Я попросил его больше для людей, которые позже просмотрят вопрос, поскольку пример кода очень полезен для таких типов вопросов. - person Konrad Wright; 29.04.2015

Две идеи:

  1. Используйте NSOperationQueue, у него есть метод setThreadPriority:, который можно использовать для ограничения нагрузки до некоторой степени.

  2. Поскольку вы выполняете диспетчеризацию в асинхронной очереди, возможно, вы можете вставить функцию sleep() после обработки каждого сотого элемента или около того. Это должно уменьшить объем работы для процессора, потому что поток останавливается в течение некоторого процента времени, но это не приведет к задержке вашего пользовательского интерфейса, потому что это просто фоновая очередь.

Очевидно, что это принесет в жертву загрузку процессора ради времени выполнения.

person Atomix    schedule 23.04.2015

Похоже, вы ничего не делаете для ограничения количества одновременных операций. Выполнение этого, вероятно, будет более эффективным, чем попытка регулировать использование ЦП, поскольку регулирование использования ЦП является расплывчатой ​​концепцией по сравнению с ней.

Чтобы ограничить количество одновременных операций, вы можете выполнить одно из следующих действий:

  1. Создайте собственный dispatch_queue с помощью dispatch_queue_create("com.myApp.loadQueue", DISPATCH_QUEUE_SERIAL). DISPATCH_QUEUE_SERIAL предотвращает одновременный запуск блоков в очереди.

  2. Как упомянул @JoJoe в своем ответе, используйте NSOperationQueue. Я бы предложил использовать setConcurrentOperationCount:, а не setThreadPriority:, чтобы ограничить количество одновременных операций. Если вы не хотите возиться с настройкой собственного подкласса NSOperation, вы можете использовать NSBlockOperation.

person damian    schedule 27.04.2015