Подтвердить что ты не робот

Есть ли какое-либо конкретное исследование влияния эффективности использования ARC?

Я не смог найти объективное исследование влияния производительности ARC в реальном проекте. Официальный документ сообщает

Компилятор эффективно устраняет многие посторонние вызовы сохранения/выпуска и прилагает большие усилия для ускорения работы в среде Objective-C в целом. В частности, общий шаблон "вернуть сохраненный/автореализованный объект" намного быстрее и фактически не помещает объект в пул автозаполнения, когда вызывающим объектом метода является код ARC.

который был передан/деформирован tech-fanboys в "ARC быстрее".

То, что я точно знаю, это то, что я измерил. Недавно мы перенесли проект iOS в ARC, и я сделал некоторые измерения производительности до/после в некоторой области интенсивного процессора нашего кода (производственный код, скомпилированный с флагом -Os, конечно).

Я наблюдал регрессию производительности 70% (да 70%). Используя инструменты для отслеживания событий сохранения/выпуска, я понял, что компилятор вводит много пар сохранения/выпуска в области, где вы этого не сделали бы (в среде pre ARC). В принципе, любое временное становится сильным. То есть, я считаю, источником регрессии производительности.

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

К счастью, инструменты смогли показать мне самый дорогостоящий удержание/выпуск, введенный ARC, и я смог отключить их, используя __unsafe_unretained. Это немного снижает регрессию производительности.

Есть ли у кого-нибудь информация об этой или иной технике, чтобы избежать потери производительности? (кроме деактивации ARC)

Спасибо.

EDIT: Я не говорю, что ARC плохо из-за воздействия производительности. Преимущество использования ARC в значительной степени превосходит регрессию производительности (в нашем коде регрессия не имела никакого видимого эффекта, поэтому я ее отпустил). Я считаю ARC очень хорошей технологией. Я никогда не вернусь в MRC. Я задаю этот вопрос больше любопытством.

Меня просто немного раздражает подавляющее большинство блогов по этой теме (например здесь и там), что более или менее создает впечатление, что код ARC будет быстрее кода MRC (что-то, к чему я верил, прежде чем я на него надел). И у меня действительно есть ощущение, что это не так, как в некоторых микро-тестах. В лучшем случае вы можете надеяться быть наравне с MRC, а не быстрее. Я сделал несколько других простых тестов, связанных с манипуляциями с объектами (например, счетным словом в документах). Каждый раз, когда ARC была медленнее (считалось не так плохо, как 70% -ная регрессия, о которой я говорил первоначально)

\ {начать сарказм}

Фактически, вышеупомянутый документ действительно ответил на вопрос:

Является ли ARC медленным?

Это зависит от того, что вы измеряете, но обычно "нет"....

который, очевидно, следует понимать как

\ начать {пародия}

Ну... гул... мы не можем сказать это медленнее, потому что это новый классный техно, и мы хотели бы, чтобы вы его приняли. Поэтому мы отвечаем "нет" двойными кавычками, чтобы избежать действия класса. И перестаньте задавать глупые вопросы.

\ конец {пародия}

\ конец {сарказм}

4b9b3361

Ответ 1

Вот мои показатели ARC и MRC. Проект тестирования производительности доступен в github, поэтому вы можете добавить свои собственные тесты. Просто не забудьте запустить его на устройстве. Результаты в симуляторе искажены, и часто в пользу MRC.

Подводя итог:

ARC и MRC обычно имеют одинаковую скорость. В общем случае код должен быть быстрее при ARC, но жесткие петли могут быть медленнее и значительно.

В тестах низкого уровня ARC имеет преимущество над MRC по скорости, благодаря оптимизации (autorelease returns, @autoreleasepool).

Существует некоторый код, в котором ARC добавляет дополнительный удержание/освобождение, которое строго не требуется в MRC, если приложение однопоточно. Такой код может быть медленнее при ARC, хотя он только делает разницу в жестких циклах и сильно зависит от кода, о котором идет речь.

Например, метод, получающий объект, должен сохранять его даже в MRC, потому что он может быть выпущен в многопоточном приложении во время работы метода. Тот факт, что вы можете опустить этот код в MRC, делает его более быстрым, но по своей сути несовместимым (хотя вы редко сталкиваетесь с такой проблемой, OTOH, если вы этого не хотите). Пример:

-(void) someMethod:(id)object
{
    [object retain]; // inserted by ARC, good practice under MRC
    [object doSomething];
    [object doAnotherThing];
    [object release]; // inserted by ARC, good practice under MRC
}

Генетический алгоритм, который я использовал в тестовом проекте, примерно на 40% медленнее с ARC из-за этого. Это плохой пример, потому что для такого алгоритма вы должны увидеть гораздо большие улучшения производительности путем перезаписи критических разделов кода в C из-за большого количества операций вставки/удаления в объектах NSMutableArray и NSNumber.

Совершенно небрежно отклонять ARC полностью, потому что в некоторых ситуациях он может быть медленнее. Если вы обнаружите, что эти ситуации являются критичными по производительности, тогда -fno-objc-arc этот код или перепишите его на C.

ARC не следует рассматривать за или против из соображений производительности. ARC - это инструмент, облегчающий работу программиста. Вам решать, хотите ли вы тратить время, пытаясь найти просочившиеся объекты и оборванных указателей, настолько сильно, что вы предпочтете придерживаться MRC.

Ответ 2

Я думаю, что если вы получите подобную регрессию производительности, единственное возможное объяснение состоит в том, что ваш управляемый вручную код был "небезопасным", я имею в виду, что существуют потенциальные утечки памяти и меньше вызовов сохранения/выпуска, что сделало управление памятью программы каким-то образом небезопасно.

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

Конечно, я думаю, что хорошо написанный вручную код может быть немного быстрее, чем ARC, но по какой цене? Намного больше работы, чтобы сделать вручную... В большинстве случаев это больше проблем, чем это стоит!

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

Однако, если у вас хорошо написана база кода MRC, и вы действительно уверены, что это безопасно и быстро, зачем ставить ее под ARC? Храните его вручную в управлении памятью, используя флаг -fno-objc-arc... Использование ARC не является обязательным, особенно по этим причинам.

Ответ 3

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

Учитывая контекст, компилятор может признать, что некоторые пары сохранения/освобождения не нужны. Однако я заметил, что даже если вызывается функция, которая объявляется статической встроенной и которая находится в той же самой трансляции, что и вызывающий, компилятор не сможет оптимизировать ненужные пары вызовов сохранения/освобождения для параметров,

Ответ 4

Конечно, временные переменные сильно отличаются по умолчанию. Это четко и четко документировано. И, если вы все продумаете, то, чего обычно захотят люди.

 MyWidget *widget=[[MyWidget alloc]init];  // 1
 [myWidget mill]; // 2

Если виджет не является сильным, новый MyWidget будет создан в строке 1 и может быть выпущен и обнулен до строки 2!

Теперь, конечно, верно, что если вы используете множество временных переменных - например, если вы строго соблюдаете Закон Деметры - в середине жесткой петли, и если вы предполагаете, что эти временные переменные вообще не имеют производительности, потому что в мире много регистров, тогда вы будете удивлены.

И это может быть угол, в котором вы сейчас живете.

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

И наоборот, ARC может оптимизировать аутсорсинг способами, которые вы не можете сделать вручную (хотя, возможно, и оптимизатор). Итак, если функция возвращает автореализованную переменную в вашем жестком цикле, вам может быть лучше с ARC.

Преждевременная оптимизация - плохая идея. Вы можете оказаться в неотвратимом углу производительности, но большинство людей - нет. Конечно, я проводил большую часть своего времени в OS X, но прошло много лет с тех пор, как у меня была проблема с производительностью, когда ответ был не лучшим алгоритмом.

(Наконец, если ARC вызывает 70% -ную производительность вашего приложения, то вы делаете очень много управления памятью на своем критическом пути! Подумайте об этом: вы тратите 70% своего времени на распределение и выпуская объекты. Это похоже на случай учебника для чего-то вроде Flyweight или кеша объектов или пула утилизации!)

Ответ 5

Я боюсь говорить что-то не по теме, а также на основе предположения...


Я думаю, что большая часть производительности от ARC заключается в том, чтобы вставлять вызовы keep/release, а не их ускорять.

Кроме того, насколько я понял, ARC обычно вводит дополнительный вызов сохранения/освобождения. Потому что ARC очень строгая и консервативная, поэтому в основном это не означает, что элита элиты остается. И многие из недавно вставленных вызовов сохранения/освобождения семантически требуются, но пропущены программистом в MRC. (Например, все параметры функции перехода и временные переменные)

Итак,

  • Количество вызовов для сохранения/освобождения фактически значительно увеличилось, чтобы строго удовлетворить семантическую завершенность.

  • Некоторые из них будут устранены очень консервативной оптимизацией.

  • Фактические вызовы для сохранения/выпуска будут включены в результате оптимизации - путем статического вызова функции C из динамического метода Objective-C invoke - поэтому сама стоимость вызова будет значительно уменьшена.

В результате мы обычно получаем снижение производительности. Я понял, что перед использованием ARC я упускаю много вызовов для сохранения/освобождения. Но, как вы указали, что бы мы ни получили, оно семантически завершено и все еще может быть вручную - таким детерминированным - устранено с помощью __unsafe_unretained.