Оптимизация производительности памяти

Из-за улучшений 64-разрядных процессоров 64-разрядные приложения имеют потенциал для выполнения быстрее, чем 32-разрядные приложения. Одновременно, 64-разрядное время выполнения увеличивает размер указателей и некоторых скалярных данных, приводящих к большему объему потребляемой памяти для Вашего приложения. Больший объем потребляемой памяти приводит к увеличенному давлению на кэши процессора и виртуальную память и может оказать негативное влияние на производительность. При разработке 64-разрядного приложения критически важно профилировать и оптимизировать использование памяти приложения.

Для всестороннего обсуждения оптимизации использования памяти см. Инструкции по Производительности Использования памяти.

Профилируйте свое приложение

Прежде, чем попытаться оптимизировать использование памяти Вашего приложения, необходимо сначала создать стандартные тесты, которые можно запустить и против 32-разрядных и против 64-разрядных версий приложения. Стандартизированные тесты могут измерить штраф за компиляцию 64-разрядной версии Вашего приложения при сравнении с 32-разрядной версией. Это также обеспечивает способ измерить улучшения, поскольку Вы оптимизируете использование памяти своего приложения. По крайней мере для одного теста используйте минимальное место — например, приложение было просто открыто и показывает пустой документ. Для других тестов включайте множество размеров данных, включая по крайней мере один тест с очень большим набором данных. (Сложное приложение может потребовать многократных наборов данных тестирования, каждый покрывающий подмножество функций приложения.) Цель для этих тестов состоит в том, чтобы измериться, варьируется ли использование памяти как изменения объема данных или тип. Если определенный вид данных заставляет 64-разрядную версию Вашего приложения использовать существенно больше памяти, чем свой 32-разрядный дубликат, который является великолепным местом, чтобы начать искать улучшения.

Проблемы использования общей памяти

Потенциальные проблемы использования памяти, с которыми можно встретиться, организованы здесь, вместе с некоторым руководством для обработки их.

Объекты основы могут быть дорогими для маленьких полезных нагрузок

Много классов в Основе предлагают гибкий набор функций, но обеспечить ту гибкость, они используют больше памяти, чем более простая структура данных. Например, использование NSDictionary объект содержать единственную пару ключ/значение является значительно более дорогим, чем простое выделение переменной для содержания данных. Создание тысяч таких словарей тратит впустую память. Используя объекты Основы, когда это не является надлежащим, не новая проблема, но при выполнении в 64-разрядное время выполнения, те объекты использование еще больше памяти.

Выберите компактное представление данных

Найдите места, где можно использовать лучшее представление данных, чем Вы имеете. Например, Вы храните календарную дату с помощью следующей структуры данных:

struct date
{
    NSInteger second;
    NSInteger minute;
    NSInteger hour;
    NSInteger day;
    NSInteger month;
    NSInteger year;
};

Эта структура 24 байта длиной; в 64-разрядное время выполнения требуется 48 байтов, только для даты! Более компактное представление просто хранит число секунд с определенного времени, такой как time_t тип данных, используемый ANSI C. Когда необходимо, Вы преобразовываете это компактное представление календарной дате и время.

struct date
{
    time_t seconds;
};

Обратите внимание на то, что time_t когда Ваше приложение компилируется в течение 64-разрядного времени выполнения, тип данных изменяет размер.

Упакуйте структуры данных

Для выравнивания структур данных компиляторы иногда добавляют дополнение к структуре. Например:

struct bad
{
    char       a;        // offset 0
    int32_t    b;        // offset 4
    char       c;        // offset 8
    int64_t    d;        // offset 16
};

Эта структура включает 14 байтов данных, но из-за дополнения, это приводит 24 байта в рабочее состояние пространства.

Лучший проект сортирует поля от самого большого до самого маленького выравнивания.

struct good
{
    int64_t    d;        // offset 0
    int32_t    b;        // offset 8
    char       a;        // offset 12;
    char       c;        // offset 13;
};

Эта версия не добавляет дополнительного дополнения.

Используйте меньше указателей

Избегите злоупотреблять указатели в коде. Рассмотрите следующую структуру данных:

struct node
{
    node        *previous;
    node        *next;
    uint32_t    value;
};

Когда эта структура компилируется в течение 32-разрядного времени выполнения, только 4 байта из 12 используются в качестве полезной нагрузки — остальное используется для соединения. При компиляции той же самой структуры в течение 64-разрядного времени выполнения структура берет 20 байтов — одни только ссылки составляют 80% используемой памяти. Рассмотрите использование массивов или составных типов и индексов хранения вместо этого.

Выделения памяти дополняются для сохранения выравнивания

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

Кэш только, когда Вы должны

Кэширование ранее расчетных результатов является распространенным способом улучшить производительность приложения. Однако, можно хотеть заняться расследованиями, помогает ли кэширование действительно приложению. Поскольку предыдущие примеры показали, использование памяти выше в 64-разрядных системах. Если Ваше приложение полагается слишком много на кэширование, давление, это ставит систему виртуальной памяти, может фактически привести к худшей производительности.

Типичные примеры способов поведения избежать включают:

  • Кэширование любых данных, которые класс может дешево повторно вычислить на лету

  • Кэширование данных или объектов, которые можно легко получить из другого объекта

  • Кэширование системных объектов, которые недороги для воссоздавания

  • Кэширование данных только для чтения, через которые можно получить доступ mmap()

Всегда тест, чтобы гарантировать, что кэширование улучшает производительность Вашего приложения. И обязательно встройте рычаги в Ваше приложение так, чтобы можно было выборочно отключить кэширование. Таким образом можно протестировать, имеет ли отключение определенного кэша значительный эффект на использование памяти или производительность приложения. Удостоверьтесь, что Вы тестируете много различных наборов данных на свои алгоритмы кэширования.