Сравнение производительности NSEnumerator и цикла for в Cocoa

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


person Heat Miser    schedule 28.08.2008    source источник


Ответы (3)


Использование нового синтаксиса for (... in ...) в Objective-C 2.0, как правило, является самым быстрым способом перебора коллекции, поскольку он может поддерживать буфер в стеке и получать в него пакеты элементов.

Использование NSEnumerator, как правило, является самым медленным способом, поскольку оно часто копирует итерируемую коллекцию; для неизменяемых коллекций это может быть дешево (эквивалентно -retain), но для изменяемых коллекций это может привести к созданию неизменяемой копии.

Выполнение вашей собственной итерации — например, с использованием -[NSArray objectAtIndex:] — обычно находится где-то посередине, потому что, хотя у вас не будет потенциальных накладных расходов на копирование, вы также не будете получать пакеты объектов из базовой коллекции.

(PS — этот вопрос должен быть помечен как Objective-C, а не C, поскольку NSEnumerator — это класс Cocoa, а новый синтаксис for (... in ...) специфичен для Objective-C.)

person Chris Hanson    schedule 28.08.2008

После запуска теста несколько раз результат практически одинаков. Каждый блок измерений выполняется 10 раз подряд.

Результат в моем случае от самого быстрого к самому медленному:

  1. For..in (testPerformanceExample3) (0,006 с)
  2. Пока (testPerformanceExample4) (0,026 с)
  3. For(;;) (testPerformanceExample1) (0,027 с)
  4. Блок перечисления (testPerformanceExample2) (0,067 с)

Циклы for и while почти одинаковы.

сравнение итераций

tmp — это NSArray, который содержит 1 миллион объектов от 0 до 999999.

- (NSArray *)createArray
{
    self.tmpArray = [NSMutableArray array];
    for (int i = 0; i < 1000000; i++)
    {
        [self.tmpArray addObject:@(i)];
    }
    return self.tmpArray;
}

Весь код:

ViewController.h

#import <UIKit/UIKit.h>

@interface ViewController : UIViewController

@property (strong, nonatomic) NSMutableArray *tmpArray;
- (NSArray *)createArray;

@end

ViewController.m

#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self createArray];
}

- (NSArray *)createArray
{
    self.tmpArray = [NSMutableArray array];
    for (int i = 0; i < 1000000; i++)
    {
        [self.tmpArray addObject:@(i)];
    }
    return self.tmpArray;
}

@end

MyTestfile.m

#import <UIKit/UIKit.h>
#import <XCTest/XCTest.h>

#import "ViewController.h"

@interface TestCaseXcodeTests : XCTestCase
{
    ViewController *vc;
    NSArray *tmp;
}

@end

@implementation TestCaseXcodeTests

- (void)setUp {
    [super setUp];
    vc = [[ViewController alloc] init];
    tmp = vc.createArray;
}

- (void)testPerformanceExample1
{
    [self measureBlock:^{
        for (int i = 0; i < [tmp count]; i++)
        {
            [tmp objectAtIndex:i];
        }
    }];
}

- (void)testPerformanceExample2
{
    [self measureBlock:^{
        [tmp enumerateObjectsUsingBlock:^(NSNumber *obj, NSUInteger idx, BOOL *stop) {
           obj;
        }];
    }];
}

- (void)testPerformanceExample3
{
    [self measureBlock:^{
        for (NSNumber *num in tmp)
        {
            num;
        }
    }];
}

- (void)testPerformanceExample4
{
    [self measureBlock:^{
        int i = 0;
        while (i < [tmp count])
        {
            [tmp objectAtIndex:i];
            i++;
        }
    }];
}

@end

Для получения дополнительной информации посетите: Apples "О тестировании с помощью Xcode"

person Zsivics Sanel    schedule 08.06.2015
comment
Ваш вывод верен, но тест for(;;) может быть быстрее, если не вызывать [tmp count] после каждого цикла. - person Jesse Gumpo; 28.01.2017
comment
Да, вы правы, в этом случае результат теста for(;;) равен 0,023, а результат теста цикла while равен 0,022 - person Zsivics Sanel; 28.01.2017

Они очень похожи. В Objective-C 2.0 для большинства перечислений теперь по умолчанию используется значение NSFastEnumeration, которое создает буфер адресов для каждого объекта в коллекции, который затем может быть доставлен. Один шаг, который вы экономите по сравнению с классическим циклом for, заключается в том, что вам не нужно вызывать objectAtIndex:i каждый раз внутри цикла. Внутренности перечисляемой вами коллекции реализуют быстрое перечисление без вызова objectAtIndex:i method.

Буфер является одной из причин того, что вы не можете изменить коллекцию во время перечисления, адреса объектов изменятся, и созданный буфер больше не будет соответствовать.

В качестве бонуса формат в версии 2.0 выглядит так же красиво, как и классический цикл for:

for ( Type newVariable in expression ) { 
    stmts 
}

Прочтите следующую документацию, чтобы узнать больше: Справочник по протоколу NSFastEnumeration< /а>

person Community    schedule 28.08.2008
comment
@Джефф Этвуд, эта ссылка у меня не работает. Вместо этого я только что установил Xcode 4. Я искал NSFastEnumeration в документации Xcode и справочнике API, чтобы найти справочник протокола NSFastEnumeration. - person ma11hew28; 17.04.2011
comment
@matt в будущем нажмите «Изменить» и сделайте пост лучше для наших попутчиков. Я не занимаюсь разработкой iOS.. :) - person Jeff Atwood; 17.04.2011