8 августа 2013 в 13:07

Почему веб-приложения на мобильных платформах работают медленно перевод

От переводчика
Это перевод статьи Drew Crawford «Why mobile web apps are slow», опубликованной 09 июля 2013. Статья очень интересная, но большая — ошибки возможны — прошу простить и присылать замечания в личку.
Поскольку затронута острая тема, прошу заметить, что переводчик не обязательно разделяет мнение автора статьи!
При переводе текст слегка видоизменялся, поскольку прямой перевод не всегда понятно передает смысл. Для перевода термина «native code» был использован англицизм «нативный код», который понятнее и короче, чем «родной для платформы код». Термин «word processing» переводится как «верстка текста», хотя это немного сужает первоначальный смысл. Термин managed код («управляемый код») не был переведен, так как удачного перевод (на взгляд переводчика) не существует. Под «терминированием» приложения подразумевается его принудительное завершение операционной системой.
Повествование в статье ведется от первого лица: автора статьи.



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

В этой статье я собираюсь привести фактические подтверждения, связанные с обсуждаемой проблемой, вместо того, чтобы играть в игру «кто кого перекричит». Вы увидите сравнительные тесты, услышите мнения экспертов и даже сможете прочесть «искренние-как-на-исповеди» заметки из тематических журналов. В этой статье более 100 цитат, и это — не шутка. Я не гарантирую, что эта статья вас переубедит, более того, даже не гарантирую, что все в ней абсолютно достоверно (это невозможно сделать в статье такого размера), но зато я могу гарантировать, что это наиболее полный и объективный подход к проблеме, которую уже осознали многие iOS разработчики: что веб-приложения на мобильных платформах работают медленно и будут работать медленно в обозримом будущем.

Сразу хочу предупредить — статья нереально большая, более 10000 слов. Так оно и задумывалось. Последнее время я предпочитаю содержательные статьи популярным. Эта статья — вклад в копилку содержательных статей — как попытка практиковать то, что я сам ранее проповедовал: следует поощрять глубокие, основанные на доказательной базе дискуссии в противовес написанию пустых, но остроумных комментариев.

Статья написана сухим языком, потому что исследуемая тема уже много раз обсасывалась в разных формах. Если вы хотели прочесть 30-секундный треп на тему «мобильные веб-приложения сосут!»/«черт! нет», эта статья не для вас (от переводчика: здесь в исходной статье приводится масса ссылок на подобные англоязычные обсуждения). С другой стороны, насколько мне известно, не существует полного, объективного, доказательного обсуждения этой проблемы в Сети. Может и глупая затея, но эта статья является попыткой говорить доказательно и содержательно о проблеме, 100% обсуждений которой обычно сводятся к холивору. В свою защиту могу сказать, что верю, что это происходит не из-за самой проблемы, а из нежелания улучшить качество дискусии ее участниками. Полагаю, что мы выясним, так ли это.

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

КРАТКИЙ ОБЗОР


Моя предыдущая статья, основываясь на тестах производительности SunSpider, утверждала, что, на текущий момент, веб-приложения на мобильных платформах работают медленно:
«Если под 'веб-приложением' подразумевается 'страница с парой кнопок', то уютные тесты производительности, подобные SunSpider, вполне сгодятся. Но заниматься легковесной обработкой фотографий, легковесной версткой текста, хранением данных на стороне пользователя и анимацией между экранами в веб-приложении, запущенном на архитектуре ARM, следует только по одной причине: если вашей жизни угрожает смертельная опасность.»

Вам следует прочесть ту статью, но, в любом случае, вот результаты тестов:
image
Есть три существенные категории возражений по поводу этого графика:
1. Факт, что JS работает медленнее нативного кода, не является новостью: это известно всем со времен CS1 и обсуждений компилируемых, JIT и интерпретируемых языков. Вопрос лишь в том — не слишком ли медленно JS работает для вашей конкретной задачи — и синтетические тесты не дают ответа на этот вопрос.
2. Да, JS работает медленее и это мешает, но он становится все быстрее и рано или поздно он станет настолько быстр, что потянет вашу задачу (см. пункт 1), так что пора начинать инвестировать в JS.
3. Я пишу серверный код на Python/PHP/Ruby и понятия не имею, о чем это вы все говорите. Я знаю, что мои серверы побыстрее, чем ваши мобильники, но я-то уверенно держу X,000 пользователей, используя интерпретируемый язык программирования, а вы не можете сообразить, как одного удовлетворить на JIT-языке?

У меня заоблачная цель — хочу опровергнуть все эти три утверждения в этой статье! Да, JS медленный настолько, что становится непригодным; нет, он не станет существенно быстрее в ближайшие годы; нет, ваш опыт серверного программирования на скриптовых языках невозможно перенести адекватно на мобильные платформы.

Как правило, в подобных статьях «слона-то и не приметили», то есть никто не попробовал реально измерить насколько JS медленнее или предложил бы полезный, пригодный способ это сделать (ну вы понимаете… медленный, по сравнению с чем?). В этой статье я приведу не один, а целых три способа измерения эквивалентности производительности по отношению к производительности JS. Так что, не просто буду болтать языком «бла бла JS тормозит», но приведу измерительные сравнения по отношению к нескольким задачам, с которыми вы реально сталкиваетесь как разработчики, так что сможете сами посчитать — хватит ли производительности для вашей конкретной задачи.

ЛАДНО, НО КАК ТОЧНО ИЗМЕРИТЬ ПРОИЗВОДИТЕЛЬНОСТЬ JAVASCRIPT ПО ОТНОШЕНИЮ К НАТИВНОМУ КОДУ?


Хороший вопрос. Чтобы дать на него ответ, я выбрал случайный тест производительности у The Benchmarks Game. Затем нашел старую C программу, которая делает тоже самое (старую, поскольку новые содержат всякие x86 оптимизации). Затем померял производительность Nitro (от переводчика: JS-движок Safari) по отношению к LLVM на моем верном iPhone 4S. Весь код доступен на GitHub.

Да, это все очень случайный код. Но и в реальной жизни код не менее случаен. Хотите провести эксперимент получше — флаг вам в руки. Я провел этот эксперимент просто по той причине, что других подобных (сравнивающих Nitro и LLVM) не нашел.

В любом случае, в данном синтетическом тесте LLVM (от переводчика: то есть нативный код) примерно в 4.5 раз быстрее Nitro (от переводчика: JavaScript-кода).
image

Так что, если вы задаетесь вопросом — «насколько быстрее работает моя функция, исполняемая нативно, в сравнении с исполнением ее на Nitro JS», то ответ такой: она работает примерно в 5 раз быстрее. Этот результат приблизительно соответствует результатам Benchmarks Game, где сравниваются x86/GCC/V8. Они утверждают, что GCC/x86 работает в среднем от двух до девяти раз быстрее чем V8/x86. Так что полученный мной результат не выпадает из картины мира, причем верен и для архитектуры ARM, и для x86.

НО РАЗВЕ 640 КБ ПАМЯТИ 1/5 ПРОИЗВОДИТЕЛЬНОСТИ НЕДОСТАТОЧНО ДЛЯ ВСЕХ?


Вполне достаточно на x86. Ну, правда, ну насколько же ресурсоемко вывести табличку? Не сильно. Одна проблема: ARM — это не x86.

Согласно GeekBench, последний MacBook Pro быстрее последнего iPhone на порядок. Ну и ладно, на таблички все равно хватит. Достаточно нам 10% производительности. Секунду! Вы эти 10% не забыли поделить еще на 5? Итак, в сухом остатке имеем всего 2% производительности от десктопа (я немного небрежно округляю единицы измерения, но для наших рассуждений точности хватит).

Ну, хорошо, но насколько в действительности ресурсоемким является верстка текста? Разве ею не занимались еще на m68k? Что ж, на этот вопрос легко ответить. Может вы и не помните, но совместная работа с документами появилась в Google Docs не сразу. Они значительно переписали код и добавили эту фичу в апреле 2010 года. Давайте посмотрим на производительность браузеров в далеком 2010:
image

Этот график отчетливо показывает, что iPhone 4S не может соперничать с браузерами на момент времени, когда Google добавил совместную работу с документами. Ну, то есть IE8 он обгоняет, поздравляем!

Давайте посмотрим на еще одно серьезное JS-приложение: Google Wave. Wave никогда не работала в IE8 (согласно Google), потому что он был слишком медленный.
image

Обратите внимание, что поддерживаемые браузеры уложились в 1000, а тот, который выдал 3800, был исключен, потому что медленный. iPhone выдает 2400. И, как и IE8, недостаточно производителен, чтобы запустить Wave.

Для ясности: совместная работа с документами на мобильных устройствах — возможна. Но только не на JS. Разница в производительности между нативным и веб приложением сравнима с разницей производительности между FireFox и IE8 и является слишком большой для серьезной работы.

НО МНЕ КАЗАЛОСЬ, ЧТО ПРОИЗВОДИТЕЛЬНОСТЬ V8/СОВРЕМЕННОГО-JAVASCRIPT СРАВНИМА С ПРОИЗВОДИТЕЛЬНОСТЬ ПРИЛОЖЕНИЯ НА C?


Зависит от того, что считать «сравнимым». Если C-программа выполняется за 10 мс, то 50 мс программа на JS может считаться «сравнимой». Но если C-приложение занимает 10 сек, то 50 сек JS приложение для большинства уже несравнимо с ним.

ВЗГЛЯД СО СТОРОНЫ АППАРАТНОГО ОБЕСПЕЧЕНИЯ


Отставание в 5 раз, вообщем, не страшно на платформе x86, потому что x86, для начала, на порядок быстрее ARM. Есть запас. Решение просто: повысить производительность ARM в 10 раз и — вуаля — получим производительность десктопа на мобильном девайсе.

Осуществимость этого так или иначе крутится вокруг вашей веры в Закон Мура, который пытается ускорить чип, работающий на 80 г батарейке. Не являясь инженером аппаратного обеспечения, я работал на крупную полупроводниковую компанию и ее работники говорили мне, что производительность в текущий момент это результат техпроцесса (вот этой вот фигни, которую меряют в нанометрах). Впечатляющий рост производительности iPhone 5 в большей степени вызван уменьшением техпроцесса — с 45 нм до 32 нм — примерно на треть. Чтобы повторить этот шаг Apple придется уменьшить техпроцесс до 22 нм.

На всякий случай, отметим, что Intel-овский процессор Atom следующего поколения (Bay Trail) — на техпроцессе 22 нм — еще не существует. И Intel пришлось изобрести совершенно новый тип транзистора, так как старый просто не мог работать на 22 нм. Как думаете они лицензируют эту технологию ARM? Подумайте снова. Есть только планы по строительству 22 нм фабрик и большинство их контролируется Intel.

На самом деле, ARM уменьшит техпроцесс до 28 нм в следующем году или около того (следите за A7), а Intel готовится к переходу на 22 нм (а может и на 20 нм) чуть позже. На аппаратном уровне я гораздо больше готов поверить в том, что x86 вставят в мобильник раньше, чем появится ARM-чип со сравнимой производительностью.

Замечания от бывшего инженера Intel:
Я — бывший инженер, который работал над микропроцессорами для мобильной платформы и позже над Атомами. Мое (невероятно предвзятое) мнение: проще будет воткнуть x86 в телефон, отказавшись от части функционала, чем нарастить производительность ARM до сравнимой с x86, добавляя в него функционал с нуля.

Замечание от инженера робототехники:
Вы абсолютно правы, когда говорите, что не будет значительного роста производительности, и что Intel получит более производительный мобильный процессор только через несколько лет. На самом деле, мобильные процессоры уперлись в предел, в который раньше уперлись десктопные процессоры, когда достигли частоты ~3 Ghz: дальнейшее увеличение частоты влечет за собой резкий рост потребляемой мощности и это правило будет действовать и для следующих процессов, хотя они немного увеличат IPC (на 10-20 %). Упершись в этот предел, десктопные процессоры стали многоядерными, но мобильные системы-на-чипе уже многоядерные, так что легкой прибавки производительности не будет.


Так что, может Закон Мура и выполняется, но при условии, что вся мобильная экосистема перейдет на x86. Это не является абсолютно невозможным — так делали раньше. Но в тот момент продажи составляли примерно миллион устройств в год, а сейчас за один квартал продается 62 миллиона. Это было сделано с помощью виртуализации, которая эмулировала старую архитектуру на уровне 60% производительности, в то время как современные гипотетические виртуализации для оптимизированного (O3) ARM кода ближе к 27%.

Чтобы верить, что производительность JS рано или поздно перестанет быть проблемой, проще всего идти по аппаратному пути. Либо в течение лет пяти у Intel появится жизнеспособный чип для iPhone (что вероятно) и Apple перейдет на него (что маловероятно), либо ARM-ы подтянутся за следующую декаду (спросите 10 инженеров на этот счет и получите 10 мнений). Но декада все же большой срок, на мой взгляд, для чего-то, что выстрелит.

Боюсь, что мое знание аппаратной части здесь заканчивается. Если хотите верить, что ARM догонит x86 за 5 лет, то первый шаг — найти кого-то, кто работает на ARM или Intel и кто согласится с вами. Я со многими подобными инженерами проконсультировался и все они отклонили это утверждение. Из чего следует, что этого, скорее всего, не произойдет.

ВЗГЛЯД СО СТОРОНЫ ПРОГРАММНОГО ОБЕСПЕЧЕНИЯ


Многие компетентные программисты совершают одну и ту же ошибку. Они размышляют следующим образом: JavaScript уже стал намного быстрее, чем был! И он будет становится все быстрее и быстрее!

Посылка верна. JavaScript серьезно ускорился. Но мы находимся в данный момент на пике производительности JavaScript. Дальнейшее ускорение вряд ли возможно.

Причина? Для начала, отметим, что большинство улучшений JS за его историю на самом деле связаны с аппаратной частью. Jeff Atwood пишет:
Я обнаружил, что производительность JavaScript выросла с 1996 по 2006 в 100 раз. Построение Web 2.0 «на скелете» JavaScript стало возможным, в основном, по причине роста производительности по закону Мура.

Если связать рост производительности JS с ростом производительности аппаратной платформы, то, не следует ожидать значительного роста программной производительности в ближайшем будущем. Хотите или нет, но если вы верите, что скорость JS возрастет, то наиболее вероятно, что это произойдет из-за роста аппаратной производительности, поскольку таковы исторически тренды.

Как насчет JIT (от переводчика: виртуальные машины, ускорившие исполнение JS)? V8, Nitro/SFX, TraceMonkey/IonMonkey, Chakra и прочая? Ну, в момент своего появления они сыграли свою роль, но все же не настолько большую, как вам хотелось бы думать. V8 вышел в свет в сентябре 2008 года. Я раскопал копию Firefox 3.0.3 примерно из того же промежутка времени:
image
Вы только не поймите меня неправильно — 9-ти кратный рост производительности — не шутка; в конце концов, примерно такая разница в мощности между ARM и x86. Однако, разница в производительности между Chrome 8 и Chrome 26 — это пологая кривая, потому что с 2008 года ничего революционного здесь не случилось. Остальные производители браузеров подтянулись — где-то чуть медленнее, где-то чуть быстрее, но никто не смог серьезно улучшить скорость кода с той поры.

НАБЛЮДАЕТСЯ ЛИ РОСТ ПРОИЗВОДИТЕЛЬНОСТИ JAVASCRIPT В НАСТОЯЩЕЕ ВРЕМЯ?


image
Вот Chrome 8 на моем Маке (самый ранний из тех, что еще работают, декабрь 2010). А вот Chrome 26.

Можете заметить разницу? Потому что ее нет. В последнее время не случилось ничего революционного в коде, который исполняет JavaScript.
Если вам кажется, что веб ускорился в сравнении с 2010 годом, то это, скорее всего, следствие того, что ваш компьютер работает быстрее и никак не связано с улучшениями Хрома.
ЗАМЕЧАНИЕ. Некоторые умники отметили, что тест SunSpider не годится в текущее время (не приводя конкретных доказательств). В интересах истины, я запустил Octane (старый тест производительности от Google) на старых версиях Хрома и увидел некоторые улучшения:
image
На мой взгляд, прирост производительности за этот период слишком мал, чтобы утверждать, что JS догонит нативный код за разумный период времени. Честно признаюсь, что немного перебрал: производительность JS подросла за этот период. Однако, на мой взгляд, эти цифры все равно подтверждают мою гипотезу: что JS не догонит нативный код за разумное время. Нужно увеличить производительность от 2 до 9 раз, чтобы догнать LLVM. Улучшения есть, но они недостаточны. КОНЕЦ ЗАМЕЧАНИЯ.

На самом деле, идее — ускорить JS с помощью JIT — 60 лет и за этот срок были проделаны всевозможные исследования и тысячи оптимизаций для всех мыслимых языков программирования. Но этому пришел конец, ребята, мы выжали из этих идей все, что было можно. Конец фильма. Может за последующие 60 лет придумаем что-то новое.

НО SAFARI ОЧЕВИДНО РАБОТАЕТ БЫСТРЕЕ, ЧЕМ РАНЬШЕ


Но если все так, как вы говорите, отчего постоянно мы слышим о значительном росте производительности JS? Каждую неделю кто-то говорит об очередном ускорении в очередном тесте производительности. Вот Apple утверждает, что добилась потрясающей прибавки скорости в 3.8 раз на тесте JBench:
image
Возможно, на руку Apple тот факт, что эта версия Safari еще находится под соглашением NDA (от переводчика: о неразглашении), так что никто другой не в состоянии опубликовать независимые измерения. Но позвольте мне сделать кое-какие выводы, основываясь исключительно на публичной информации.
Мне кажется довольно забавным, что заявленная Apple производительность JS в тесте JSBench гораздо выше, чем заявленная производительность в традиционных тестах, таких как SunSpider. За JSBench стоят известные личности, такие как Brenden Eich (создатель JavaScript). Однако, в отличие от традиционных тестов, JSBench не использует программы, которые умножают числа или делают что-то подобное. Вместо этого, JSBench всасывает то, что выдают Amazon, Facebook, и Twitter, и строит свои тесты производительности на этой основе. Если вы пишете браузер, который большинство используют, чтобы ходить на Facebook, то, разумеется, иметь тест, который имитирует Facebook, будет весьма полезно. С другой стороны, если вы пишете табличное приложение, игру или графический фильтр, мне кажется, традиционный тест производительности с его умножениями и вычислениями хэшей md5 будет гораздо более адекватным и полезным, чем информация о том, насколько быстро работает аналитика Facebook.
Другим важным фактом является тот, что улучшение в тесте SunSpider (по заявлениям Apple) не означает, что автоматически все остальное ускорится. В заметке, представляющей предпочитаемый Apple тест производительности, Eich и прочие пишут:
Эта диаграмма отчетливо показывает, что, согласно тесту SunSpider, производительность FireFox возросла в 13 раз от версии 1.5 до версии 3.6. Однако, если посмотреть на производительность в тесте amazon, то здесь видим умеренный рост в 3 раза. Что любопытно, за последние два года роста производительности в тесте amazon практически не наблюдается. Полагаю, что многие оптимизации, которые отлично сработали в тесте SunSpider, не играют роли для теста amazon.

В этой заметке создатель JavaScript и один из топ-архитекторов Mozilla открыто признается, что за последние два года с производительностью JavaScript в тесте Amazon не случилось вообще ничего, более того — за всю историю не случалось ничего революционного. Это пример того, как ребята из отдела по маркетингу немного переоценивают вещи в последнее время.
(Они продолжают настаивать, что тест Amazon лучше помогает предсказать время работы Amazon, чем тест SunSpider [ привет К.О.! ], так что он полезен для браузеров, которые люди используют, чтобы ходить на Amazon. Но ничто из этого не поможет вам написать веб-приложение для обработки фотографий).
Во всяком случае, основываясь на публично доступной информации, хочу отметить, что заверения Apple в 3.8-ном росте производительности никакой пользы вам не принесут. Также должен сказать, что, даже имея на руках тесты, в которых опровергается утверждение Apple, что Safari обгоняет Chrome, я бы не имел права их публиковать.
Давайте завершим этот раздел заключением, что даже если кто-то нарисовал график, на котором его браузер стал быстрее, это совершенно необязательно означает, что JavaScript в целом становится быстрее.
Но есть и более крупная проблема.

СПРОЕКТИРОВАН НЕ ДЛЯ ВЫСОКОЙ ПРОИЗВОДИТЕЛЬНОСТИ


image
(от переводчика: левая книга называется «JavaScript: хорошие стороны», правая — «JavaScript: полный справочник»).

Процитируем Herb Sutter, крупную величину в мире современного C++:
Этот мем уже невозможно уничтожить — «просто ждите следующего поколения (JIT или обычных) компиляторов и уж на них managed код будет работать сравнимо эффективно». Да, я жду улучшений компиляторов C# и Java — и JIT, и NGEN-подобных статических компиляторов. Но, нет, они не сотрут разницу в производительности с нативным кодом по двум причинам. Во-первых, JIT компиляция не является главной проблемой. Основная причина отставания является более фундаментальной: managed платформы были намеренно спроектированы для увеличения продуктивности программиста, даже если это шло в ущерб производительности. В частности, создатели managed платформ решили затрачивать ресурсы на определенные возможности, даже если те не используются приложением; основные из таких фич: всегда работающая сборка мусора, система исполнения виртуальной машины и метаданные. Но не только это, есть и другие примеры: в managed языках вызовы функций по умолчанию виртуальны, в то время как в C++ вызовы по умолчанию inline-ятся (от переводчика: то есть тело функции напрямую компилируется в код вместо call/ret) и ложка предотвращения inline-а стоит бочки девиртуализующей оптимизации.

Эта цитата принадлежит Miguel de Icaza из проекта Mono, который входит в довольно небольшой список «людей, разрабатывающих JIT-компилятор». Он говорит:
Это довольно точное определение отличия популярных виртуальных машин для managed языков (.NET, Java и JavaScript). Дизайнеры managed платформ предпочли безопасность производительности при их проектировании.

Или спросить Alex Gaynor, который поддерживает проект оптимизирующего JIT для Ruby и также учавствует в проекте оптимизирующего JIT для Python:
Это проклятие высокопродуктивных, динамических языков. В них слишком легко сделать хэш-таблицу. (от переводчика: полагаю, тут имеется в виду встроенная возможность таких языков по динамическому расширению объектов именованными свойствами) И это здорово, потому что C-программисты недооценивают хэш-таблицы, так как в C их трудно использовать. Для начала нет встроенного функционала. Во-вторых, использование хэш таблиц в C — это хождение по мукам. В противоположность этому, программисты на Python, Ruby, JavaScript склонны излишне часто использовать хэштаблицы, потому что это так доступно и просто… В результате, людям все равно…

Google, похоже, считает, что JavaScript уперся в стену ограничения роста производительности:
Сложные веб-приложения — на которых специализируется Google — упираются в недостатки платформы и основаны на языке, который нельзя оптимизировать и который имеет врожденные проблемы с производительностью.

Наконец, послушайте Самого. Один из моих читателей указал мне на этот комментарий Brendan Eich. Ну, знаете, это тот самый парень, который изобрел JavaScript.
Одну вещь Mike забыл упомянуть: упростите язык. Lua намного проще, чем JS. Это значит, что можно сделать более простой интерпретатор, который работает достаточно быстро, чтобы сравниться с производительностью JIT-кода (что невозможно для JS)

И немного пониже:
По поводу различий между JS и Lua, можете сказать, что дело в правильном дизайне и проектировании (ну а в чем еще?), но неотъемлемые трудности, связанные с разными направлениями дизайна, очень много значат. Вы, конечно, можете убрать ресурсоемкие места из часто исполняемых кусков кода, но они все равно останутся непроизводительными. В JS таких мест больше, чем в Lua. Один пример: в Lua (если явно не использовать metatable) нет ничего похожего на прототипную цепочку вызовов JS.

Итак, мнение о том, что JS (в частности) или динамические языки (в общем) догонят C, среди людей, которые и занимаются этим самым, является непопулярным. Есть некоторое кол-во несогласных, но никакого общего мнения — что с этим делать (и нужно ли вообще что-то делать) — нету. Но на вопрос «подтянутся ли JIT-машины в общем по производительности к программам на C» ответ людей, которые над этим работают, такой: «нет, если только не сменить язык или API».

Но существует проблема еще важнее.

ВСЯ ПРАВДА О СБОРЩИКЕ МУСОРА


image
Дело в том, что проблема производительности, связанная с вычислительной мощностью — и все тесты на этот счет, и все инженерные решения — это только одна сторона медали. Другая — это память. И, может статься, что проблема с памятью настолько велика, что вопрос вычислительной производительности — это как верхушка айсберга. На самом, деле обсуждение вычислительных проблем призвано только отвлекать внимание от настоящих проблем. Далее написано то, что, возможно, серьезно изменит ваше представление о разработке мобильных приложений.
В 2012 Apple сделала забавный и неожиданный ход (неожиданный для вас, если только вы не John Gruber и предсказывали это). Они удалили сборщик мусора из OSX. Кроме шуток, читайте руководство программиста. В правом верхнем углу жирным шрифтом написано «Не рекомендуется к использованию». Если вы пришли из мира Ruby, или Python, или JavaScript, или Java, или C#, да из любого языка, созданного после 1990, то это известие, должно быть, вас ошеломит. Не затронет, поскольку вы вряд ли пишете под Маками на ObjC. Но, все же, покажется странным. В конце-то концов, сборщики мусора уже давно с нами и проверены временем. Какого хрена их объявляют устаревшими? Вот, что говорит Apple:
Мы настолько уверены, что ARC является правильным подходом к управлению памятью, что решили отказаться от Сборщика Мусора в OSX (- Session 101, Platforms Kickoff, 2012, ~01:13:50)

О чем умалчивает эта цитата? Об аплодисментах аудитории после нее. Ладно, вот это по-настоящему странно. Вы хотите сказать, что комната забита разработчиками, которые торжественно приветствуют возвращение хаоса «эры-до-сборщиков-мусора»? Вы только представьте себе реакцию зала на заявление Matz-а об отказе от сборщика мусора на RubyConf. А эти радуются? Психи.
Не спешите списать это на эппл-бойство, такая странная реакция должна навести вас на мысль — тут что-то недосказано. И мы попробуем как раз сейчас разобраться, что именно.
Итак, мы размышляем: отказ от работающего сборщика мусора в языке — это безумие, не так ли? Наверное, попросту ARC — это специальный маркетинговый термин Apple для разукрашенного сборщика мусора, так что все эти разработчики на самом деле рады апгрейду, а не даунгрейду. Кстати, это мнение сильно распространено среди новичков в iOS.

ARC НЕ ЯВЛЯЕТСЯ СБОРЩИКОМ МУСОРА


Всем, кто уверен, что ARC — это сборщик мусора, врежу по физиономии вот этим слайдом от Apple:
image
(перевод слайда:
Чем НЕ является ARC:
— новым runtime для памяти;
— автоматизацией malloc, free, и пр.
— сборщиком мусора:
— нет сканирования хипов;
— нет замирания всего приложения;
— нет неопределенного освобождения памяти.
)
Никакой связи с одноименным алгоритмом сборщика мусора. Это не сборщик мусора, не похоже на сборщик мусора, работает не как сборщик мусора, нет возможностей как у сборщика мусора, ничего не сканируется, не чистится, не тормозится. Все, точка. ARC — не сборщик мусора.
Этот миф родился в тот момент, когда большая часть документации была под соглашением о неразглашении (но спеки были доступны, так что оправданием это явиться не может) и в блогосфере широко разошлось мнение, что это — правда. Это неправда. Все, прекратите уже.

СБОРЩИК МУСОРА НЕ НАСТОЛЬКО ХОРОШ, НАСКОЛЬКО ВАМ ПОДСКАЗЫВАЕТ ВАШ ОПЫТ


Вот что сказала Apple насчет ARC против GC (сборщика мусора), когда на нее надавили:
Самым главным пожеланием среди тех, что мы могли для вас сделать, является возврат сборщика мусора в iOS. И именно это мы делать не будем. К сожалению, сборщик мусора серьезно уменьшает производительность. Ваше приложение может накопить мусора и превысить допустимый лимит памяти. И сборка мусора начинается в непредсказуемые моменты времени, что приводит к пожиранию ресурса CPU и заминках в интерфейсе пользователя. Именно поэтому, GC не подходит нам на наших мобильных платформах. В противовес GС, прямое управление памятью через retain/release труднее понять и, честно говоря, геморройнее использовать. Но производительность при этом выше и более предсказуема, и поэтому прямое управление было выбрано в качестве базиса для нашей стратегии управления памятью. Потому что, в реальном мире, высокая производительность и плавный интерфейс имеют высокую ценность среди наших пользователей (~Session 300, Developer Tools Kickoff, 2011, 00:47:49).

Но это ж безумие?! Для начала:
1. Это полностью противоречит всему вашему карьерному опыту на десктопах;
2. Windows Mobile, Android, MonoTouch и многие другие похоже вполне удовлетворены GC.
Давайте и на них посмотрим.

СБОРЩИК МУСОРА НА МОБИЛЬНОЙ ПЛАТФОРМЕ ЭТО НЕ ТОЖЕ САМОЕ, ЧТО СБОРЩИК МУСОРА НА ДЕСКТОПЕ


Знаю, что вы думаете. Вы программируете на Питоне уже N лет. Сейчас 2013 год. Проблема сборки мусора полностью и бесповоротно решена.
Вот документ, который вы ищете. Похоже, проблема не совсем решена:
image
Если у вас из этой статьи в голове останется что-то одно, пусть это будет этот слайд. Ось Y — это время, потраченное на сборку мусора. Ось X — это «относительный расход памяти». Относительный к чему? К минимальному требуемому размеру памяти.
Вот то, что этот слайд нам хочет сказать: «Пока у тебя в 6 раз больше памяти, чем нужно на самом деле, ты — в шоколаде. Но горе тебе, если опустишься ниже 4 размеров требуемой памяти». Необязательно верить мне на слово:
Точнее, когда у сборщика мусора есть в 5 раз больше памяти, чем требуется, его производительность совпадает или слегка превосходит прямое управление памятью. Однако, производительность сборщика мусора быстро деградирует, когда ему требуется работать с небольшими хипами. С 3-мя размерами требуемой памяти он, в среднем, работает на 17% медленнее, а с двумя размерами — на 70% медленнее. Также сборщик мусора более подвержен пейджингу, если память дефрагментирована. В подобных условиях, все протестированные нами сборщики мусора работают на порядок медленнее прямого управления памятью.

Давайте сравним с прямым (явным) управлением памятью:
Эти графики показывают, что, если приложению доступно разумное количество памяти (но недостаточное для того, что уместить приложение целиком), оба менеджера прямого управления памятью значительно превосходят по скорости все сборщики мусора. К примеру, pseudoJBB на 63 Мб доступной памяти и аллокатором Lea завершает задачу за 25 секунд. На таком же количество памяти GenMS требуется в 10 раз больше времени (255 секунд). Похожее поведение наблюдается на всех тестах, входящих в набор. Наиболее отличительный тест — 213 javac: на 36 Мб памяти с аллокатором Lea общее время работы составляет 14 секунд, в то время как время выполнения теста на GenMS составляет 211 сек, что в 15 раз дольше.

Фундаментальный вывод состоит в том, что производительность сборщика мусора в окружении с ограниченным количеством памяти резко снижается. Если вы программируете на Питоне, Руби или JavaScript на десктопах, то, скорее всего, вы всегда находитесь в правой части графика и за всю свою жизнь ни разу не столкнетесь с медленным сборщиком мусора. Переходите на левую сторону, чтобы понять с чем имеют дело остальные.

СКОЛЬКО ПАМЯТИ ДОСТУПНО ПРИЛОЖЕНИЮ НА IOS?


Точно сказать трудно. Размер физической памяти на устройствах разнится от 512 Мб на iPhone 4 до 1 Гб на iPhone 5. Но большая часть этой памяти используется системой и также под многозадачность. Единственный способ проверить — запустить в реальных условиях. Jan Ilavsky создал приложение для этой цели, но похоже никто до сих пор не публиковал результаты. До сегодняшнего дня.
Важно измерять память в «нормальных» условиях, потому что, если делать это сразу после загрузки, то результаты будут лучше, поскольку нет расходов на страницы, открытых в Safari и тому подобного. Так что я буквально взял свои девайсы, разбросанные по квартире, и прогнал тесты на них.
image
image
Чтобы увидеть детали, кликните картинку, но, собственно говоря, на iPhone 4S приложение начинает получать предупреждения о недостатке памяти, выделив примерно 40 Мб, и терминируется на (примерно) 213 Мб. На iPad 3 предупреждение на 400 Мб и терминирование на 550 Мб. Разумеется, это мои результаты — если пользователь слушает фоновую музыку, то памяти может оказаться значительно меньше, но это хоть какой-то ориентир для начала. Вроде немало (213 Мб должно хватить каждому, верно?), но с практической точки зрения недостаточно. К примеру, iPhone 4S делает снимки размером 3264×2448 пикселей. Это примерно 30 Мб данных на один снимок. Это означает получение предупреждения при наличии всего двух снимков в памяти и терминирование приложения, которое держит 7 снимков в памяти. Ах, вы хотели цикл по фотографиям в альбоме написать? Приложение будет закрыто!
Важно подчеркнуть, что на практике весьма часто один и тот же снимок хранится в нескольких местах в памяти. Например, если вы делаете снимок с камеры, то в памяти хранится 1) экран камеры, который отображает то, что она видит 2) снимок, который камера сделала 3) буфер, в котором вы сжимаете изображение в JPEG, чтобы сохранить на диск 4) версию снимка, которую вы хотите показать на следующем экране 5) версию снимка, которую вы загружаете на какой-то сервер.
В какой-то момент вы поймете, что использовать 30 Мб буфер изображения для показа превьюшек — идея не очень хорошая и добавите в приложение 6) буфер для превьюшки на следующий экран 7) буфер, в котором собственно и происходит создание превьюшки в фоне (поскольку делать это в основном потоке нельзя — слишком медленно). И потом вы вдруг поймете, что в действительности требуются превьюшки 5 разных размеров и потихоньку начнете сходить с ума. Одним словом, даже работая с единственной фотографией легко превысить доступный лимит памяти. Можете не верить мне:
Худшая идея при ограниченном количестве памяти — это кэшировать изображения. Когда изображение копируется в контекст вывода или отображается на экране, необходимо декодировать его в битмап. На каждый пиксель в битмапе отводится 4 байта, независимо от размера изображения. Этот битмап «подвешивается» к объекту с исходным изображением и его время жизни совпадает с временем жизни объекта изображения. Так что, если вы кешируете изображения, которые хотя бы раз были отображены на экране, то для каждого вы теперь храните связанный битмап. Поэтому никогда не помещайте UIImages или CGImages в кеш, если только у вас нет очень уважительной (и кратковременной) причины это делать. — Session 318, iOS Performance In Depth, 2011

Да можете даже им не верить. На самом деле, размер памяти, которую ваше приложение выделяет, является верхушкой айсберга. Серьезно, вот слайд, на котором представлен сам айсберг (Session 242, iOS App Performance – Memory, 2012):
image
(перевод слайда: НЕ ТОЛЬКО ОБЪЕКТЫ
* память хипа
— * +[NSObject] alloc]/malloc
— * объекты/буферы, выделенные фреймворками
* другая память
— * код и глобальные переменные (___TEXT, ___DATA)
— * стеки потоков
— * данные изображений
— * буфера CALayer
— * кеши баз данных
* память, вне вашего приложения
)

И эта свеча горит с обоих концов. Гораздо труднее работать с фотографиями, если у вас всего 213 Мб, в отличие от десктопа. И одновременно спрос на фото-приложения гораздо выше, поскольку у вашего десктопа нет крутой камеры и он не помещается в карман.
Возьмем другой пример. У iPad 3 разрешение дисплея, скорее всего, превышает разрешение вашего монитора (с точки зрения кинематографии этот экран находится в диапазоне между 2K и 4K). Каждый кадр изображения занимает 12 Мб. Если вы не хотите нарушать лимиты памяти, то сможете хранить примерно 45 кадров несжатого видео или анимации в памяти одновременно, что займет 1.5 секунды при 30 FPS, или .75 сек при системных 60 FPS. Случайно поместили в буфер секунду полноэкранной анимации? Приложение будет закрыто. И тут можно еще обратить внимание, что задержка в AirPlay составляет 2 секунды, так что для любого медиа приложения у вас гарантированно не хватит памяти.
И здесь опять возникает та же проблема нескольких копий изображения. К примеру, Apple утверждает, что «с каждым UIView связан CALayer и изображения слоев остаются в памяти, пока CALayer находится в иерархии объектов». Это означает, что в памяти может находится много изображений с промежуточным представлением вашей иерархии представления.
А ведь есть еще прямоугольники отсечения и буферы слоев. Эта замечательная архитектура с точки зрения экономии CPU, но ее производительность достигается за счет выделения как можно большего количества памяти. iOS спроектирована не с точки зрения экономии памяти, а с точки зрения высокой производительности. Что не очень хорошо сочетается со сборкой мусора.
И опять свеча горит с двух концов. У нас не только весьма ограниченное окружение для полноэкранной анимации. Но и огромный спрос на высококачественное видео и анимацию, поскольку это ужасная, малопамятная среда является фактически единственным форм-фактором, в котором можно приобрести высококачественный-кино-дисплей. Если вы хотите создавать программы для подобного разрешения на десктопах, то вначале убедите своих пользователей потратить $700 только на монитор. Или они могут купить iPad за $500, причем в нем уже есть и компьютер.

УВЕЛИЧИТСЯ ЛИ КОЛИЧЕСТВО ДОСТУПНОЙ ПАМЯТИ?


Некоторые умники говорят мне — «ОК, ты убедил, мы не получим более быстрый мобильный CPU. Но кол-во памяти-то вырастет, верно? Выросло же на десктопах».
Проблема этой теории заключена в том, что в ARM память находится на самом процессоре. Эта схема называется package on package (PoP) (от переводчика: чип на чипе). Так что трудности увеличения объемов памяти на самом деле аналогичны трудностям разгона CPU, поскольку сводятся к тому, как вместить больше транзисторов на чип. Транзисторы памяти вмещать немного легче, они одинаковы по размеру, но все равно трудно.
Если вы посмотрите на изображение A6 на сайте iFixit, то увидите, что почти 100% площади на поверхности чипа занято памятью. Это значит, что для увеличения памяти, надо уменьшить техпроцесс или увеличить чип. И если измерять относительно техпроцесса, то увеличение памяти всякий раз связано с увеличением размера чипа:
image
Кремний — материал не идеальный, так что с увеличением размеров идет экспоненциальный рост стоимости годных чипов. И такие чипы труднее охладить и поместить в мобильник. И у них те же проблемы, что и у CPU, поскольку память — это верхний слой чипа, в который надо впихнуть больше транзисторов.
Чего я не знаю, это отчего производители перед лицом этих проблем с PoP продолжают ее использовать. Я не нашел ARM инженера, который бы мне это объяснил. Может кто-то комментарий напишет. Возможно стоит отказаться от PoP в пользу отдельных модулей памяти как на десктопах. Но раз этого нет, значит есть какая-то причина это не делать.
Однако, мне написали несколько инженеров.
Бывший инженер Intel:
Что касается PoP. Эта архитектура серьезно повышает пропускную способность и упрощает маршрутизацию. Но я не занимаюсь ARM и могу всего не знать.

Инженер робототехники:
Когда станет не хватать PoP, «3D» память сможет дать «достаточно памяти для всех»: чипы памяти, сложенные в стопку, с возможностью разместить более 10 слоев 1 Гб памяти в том же объеме, что и сейчас. Но: затраты возрастут, а вольтаж и частоты придется уменьшить, чтобы уложиться в ограничения мощности.
Пропускная способность мобильной памяти не будет расти так быстро, как росла до этого. Она ограничена кол-вом линий между CPU и памятью. В настоящее время, большая часть периферийных соединений CPU используется для шины памяти. Средняя часть CPU не может быть использована для этого из-за технических ограничений. Следующий прорыв следует ждать от интеграции памяти прямо внутри CPU: при этом вырастет пропускная способность, будет больше свободы проектирования CPU и упадет вольтаж. И возможно появится большой быстрый кэш.

НО КАК ТОГДА MONO/ANDROID/WINDOWS MOBILE УСПЕШНО СПРАВЛЯЮТСЯ С ЭТИМ?


Есть два ответа на этот вопрос. Первый следует из графика (от переводчика: расход памяти сборщиком мусора): если у вас памяти в 6 раз больше, чем нужно, то сборка мусора идет быстро. Если, допустим, вы программируете текстовый редактор, то вполне можете уложиться в 35 Мб, что составляет 1/6 лимита памяти, на котором приложение на iPhone 4S терминируется. Так что можете взять Monо, написать редактор, убедиться, что он работает быстро и посчитать, что сборка мусора вполне шустра. И будете правы.
Да, но на Xamarin в поставку входит симулятор полетов. Выходит, что сборка мусора нормально работает и для больших мобильных приложений? Или нет?
С каким проблемами пришлось столкнуться при разработке этой игры? «Производительность была основной проблемой и продолжает оставаться таковой на всех платформах. Первые Windows Phone были довольно медленные и нам пришлось потратить много времени на оптимизацию, чтобы добиться приемлемого FPS. Эти оптимизации затронули как код симуляции, так и 3D движок. „Узким горлышком“ являются сборка мусора и слабость GPU.»

Без малейшего намека в вопросе разработчики приводят сборку мусора как одно из основных «узких мест» производительности. Если это делают люди в твоей команде разработчиков, стоит задуматься. Но может Xamarin — не типичный пример. Почитаем разработчиков на Андроиде:
Обратите внимание — приложения запускаются на моем Galaxy Nexus — назвать который медленным не поворачивается язык. Посмотрите на время, потраченное на рендеринг. На десктопе эти изображения были созданы за пару сотен мс, в то время как на мобильном устройстве на это было потрачено на два порядка больше времени. Больше 6 секунд на «inferno» (от переводчика: планета со сложным рендерингом)? Безумие!.. За это время сборщик мусора отработает 10-15 раз.

Еще:
Если вы собираетесь заниматься обработкой снимков камеры на устройствах Андроид для распознавания объектов в real-time или для создания Дополненной Реальности, вы вероятно слышали про проблему памяти превью камеры. Всякий раз, когда Java приложение получает картинку предпросмотра, выделяется новый буфер в памяти. Когда этот буфер освобождается сборщиком мусора, система замирает на 100-200 мс. Еще хуже, если система под нагрузкой (я занимаюсь распознаванием изображений — это отъедает весь CPU). Если вы посмотрите исходники Андроид 1.6, то увидите, что это происходит по той, причине, что обертка (которая защищает нас от нативного кода) выделяет новый буфер памяти на каждый новый фрейм. У встроенного нативного кода этой проблемы, конечно, нет.

Или можем почитать Stack Overflow:
Я занимаюсь улучшением производительности интерактивных игра на Java под Андроид. Время от времени в отображении или взаимодействии возникает задержка на сборку мусора. Обычно это длится меньше десятой секунды, но иногда это может занять и 200 мс на медленных устройствах… Если во внутреннем цикле мне вдруг понадобятся хэши, я знаю, что мне надо быть осторожным или вообще переписать их самому вместо использования фреймворка Java Collections поскольку не могу себе позволить лишние сборки мусора.

Вот самый значимый ответ, 27 голосов «за»:
Я занимался мобильными играми на Java… Лучший способ избежать сборки мусора (которая будет происходить в произвольный момент времени и убьет производительность игры) — вообще не создавать объекты в основном цикле. Другого пути нет… Только хардкор ручное управление объектами, увы. Так делают все основные высокопроизводительные игры на мобильных устройствах.

Послушаем Jon Perlow из Facebook:
Сборщик мусора является огромной проблемы для производительности при разработки «плавных» приложений на Андроид. В Facebook одной из основных проблем является замирание потока UI при сборках мусора. Когда вы обрабатываете большое число битмапов, сборка мусора происходит часто и избежать ее невозможно. Одна такая сбора приводит к потере нескольких фреймов. Даже если сборка заблокирует основной поток всего на несколько миллисекунд, это может не позволить сгенерить фрейм в отведенные на это 16 мс.

Почитаем Microsoft MVP:
В нормальной ситуации ваш код отображения уложится в 33.33 мс (от переводчика: на один кадр), поддерживая приемлемые 30 FPS… Однако, сборка мусора отнимает время. Если вы не мусорили на хипе, сборка отработает быстро и не составит проблемы. Но поддерживание хипа «в чистоте», достаточной для быстрой отбработки сборщика мусора — это сложная программистская задача, которая требует тщательного планирования и переписывания кода и при этом все равно не гарантирует устойчивую работу (как правило, в сложной игре на хипе хранится очень много всего). Проще (если возможно) ограничить или, вообще, не выделять память во время процесса игры.

Лучший способ обыграть сборщик мусора — вообще не играть. Это утверждение (в более слабо выраженной форме) находится в официальной документации по Андроид:
Создание объекта не является бесплатным. Сборщик мусора, использующий поколения и пулы на каждый поток, может ускорить выделение памяти, но выделение памяти всегда дороже не выделения памяти. По мере того, как вы выделяете все больше объектов, вы столкнетесь с периодической сборкой мусора, которая вызывает небольшие задержки интерфейса. Сборщик мусора, который появился в Андроид 2.3, работает лучше, но все равно следует избегать ненужной работы. Так что не создавайте объекты, которые вам не нужны… Точнее, не стоит создавать временные объекты, если в этом нет необходимости. Меньше объектов, меньше сборки, что положительно влияет на юзабилити.

Все еще не убеждены. Давайте спросим инженера по Сборке Мусора. Который пишет сборщики. И зарабатывает себе на хлеб этим самым. Короче, это тот парень, которому по работе приходится это все знать.
Однако, с появлением WP7 возможности устройств в терминах производительности CPU и памяти резко выросли. Появились игры и приложения на Silverlight, занимающие по 100 Мб памяти. По мере увеличения доступной памяти число объектов выросло экспоненциально. По схеме (описанной выше) сборщик мусора должен обойти каждый объект и все его ссылки, чтобы пометить их (mark) и позднее очистить (sweep). Так что время, потраченное на сборку мусора, также значительно возросло и стало зависеть от размера «ссылочной сети» приложения. Это привело к очень длительным паузам в случаях больших XNA игр и SL приложений, что приводит к длительной их загрузке (поскольку сборка работает и в это время) и подергиваниям во время игры/анимации.

Все еще не убеждены? У Хрома есть тест, который измеряет производительность сборки мусора. Посмотрим как он справляется…
image
Видим немалое число остановок, потраченных на сборку мусора. Да, это стресс-тест, но все же. Вы действительно готовы ждать 1 секунду при рендеринге фрейма? Вы с ума сошли.

СЛУШАЙ, ЦИТАТ СЛИШКОМ МНОГО, НЕ МОГУ ИХ ВСЕ ПРОЧЕСТЬ. ДАВАЙ УЖЕ ЗАКЛЮЧЕНИЕ.


Вот заключение: на мобильных устройствах управление памяти дается непросто. iOS сформировала культуру ручного управления большинством вещей, при этом компилятор выполняет простые вещи. Андроид продвигает улучшенную сборку мусора, которой они сами же изо всех сил стараются не пользоваться. В любом случае, при написании приложений под мобильные устройства надо поломать голову над управлением памятью. Этого просто невозможно избежать.
Когда программисты JavaScript или Ruby или Python слышат про «сборку мусора», они думают, что это «сборка мусора» как «палочка-выручалочка». Они думают «сборка мусора дает мне возможность не думать об управлении памятью». Но на мобильных устройствах такой «палочки-выручалочки» нету. Любой программист мобильных устройств вынужден решать задачи управления памятью, независимо от того — есть сборка мусора, или ее нет. Сборка мусора может стать «палочкой-выручалочкой» на мобильных устройствах только тем же способом, что и на десктопах — затратив в 10 раз больше памяти, чем требуется.
Весь дизайн JavaScript основан на утверждении, что о памяти беспокоится не следует. Спросите у разработчиков Хромиума:
есть ли хоть какой-то способ заставить движок хрома выполнить сборку мусора? Вообще говоря — нет и это сделано намеренно.

Спецификация ECMA не содержит слова «выделение памяти» (allocation), единственная ссылка на «память» утверждает, что это полностью зависит от «окружения» (host-defined).
Вики по ECMA версии 6 содержит несколько драфтовых страниц, которые сводятся к следующему (я не шучу):
сборщик мусора НЕ ДОЛЖЕН собирать память, содержимое которой может понадобится для продолжения корректного выполнения программы… Все объекты, которые (сильно) недоступны от корня ДОЛЖНЫ быть очищены, если для продолжения корректной работы приложению не хватает памяти.

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

По-русски: философия JavaScript (если она вообще есть) заключается в том, что вы не имеет никакого представления о том, что творится в системной памяти, точка. Это настолько невероятно расходится с тем, как в реальности создаются мобильные приложения, что я даже не могу подобрать слов, чтобы выразить свое отношение. Смотрите: во вселенной iOS люди не верят в сборку мусора и считают, что программисты Андроид сошли с ума. Подозреваю, что во вселенной Андроид разработчики считают, что программисты iOS сошли с ума, раз занимаются ручным управлением памятью. Но есть одно мнение, с которым согласны оба лагеря. Программисты JavaScript безумны в квадрате. Есть примерно нулевая вероятность, что вы сможете написать мобильное приложение, не заботясь о расходе памяти. Так что, отложив в сторону тесты производительности SunSpider и прочие вещи, связанные с CPU, мы приходим к заключению, что JavaScript в настоящее время фундаментально противоположен концепции «тщательное-управление-памятью», которая является абсолютно необходимой при программировании под мобильные устройства.
До тех пор, пока пользователи хотят от мобильных устройств видео и фото приложений, которых и на десктопах-то никогда и не было, и до тех пор, пока на мобильных устройствах намного меньше памяти, эта проблема является неразрешимой. Требуются серьезные гарантии управления памятью на мобильных устройствах. И JavaScript не дает их, намеренно.

А ЕСЛИ ВДРУГ ДАСТ?


Теперь вы можете сказать — «ладно, JavaScript парни живут на десктопах и не знакомы с проблемами мобильных приложений. Но может их переубедили. Или кто-то, кто занят мобильными приложениями, форкнул JavaScript. В теории, из этого может что-то выйти?»
Не уверен, что эта задача легко решается, но могу попробовать обрисовать границы. Есть группа людей, которые попробовали форкнуть динамический язык, чтобы он подходил для разработки на мобильных устройствах — и называется он RubyMotion.
Это умные люди, которые хорошо знают Ruby. И эти люди решили, что сборка мусора для их форка языка — это плохая идея (слышите защитники сборки мусора?). Так что они взамен привили к языку нечто, очень похожее на ARC. Но похоже не очень-то вышло:
Заключение: многие разработчики столкнулись с проблемами с памятью, которые являются результатом RM-3 (от переводчика: баг RubyMotion, по утверждениям разработчиков был пофиксен 11 июля 2013) или еще каких-то невыявленных проблем, связанных с выделением памяти в RubyMotion.

Ben Sheldon отмечает:
Не только у вас. Я сам столкнулся с крашами, связанными с памятью (типа SIGSEGV и SIGBUS), которые проявляются у 10-20% пользователей.

Есть определенные сомнения насчет того, можно ли отследить проблему:
Я поднял вопрос о RM-3 на последней встрече и ответили и Laurent, и Watson. Watson заметил, что RM-3 — это самый трудно исправляемый баг, Laurent рассказал, что он попробовал несколько подходов, но ни один не помог. Оба являются сильными разработчиками, которым можно верить.

Есть также сомнение, решается ли это вообще:
Долгое время я считал, что блоками легко может управлять компилятор, то есть содержимое блока можно статически проанализировать, чтобы определить, что блок ссылается на переменную вне своей видимости. Я полагал, что все подобные переменные можно занять при создании блока и освободить при уничтожении блока. Что привяжет время жизни переменной ко времени жизни блока (не полное время жизни, конечно). Но есть одна проблема: instance_eval. Содержимое блока может использоваться раньше времени.

У RubyMotion есть еще проблема — утечка памяти. И может есть и другие проблемы. Никто не может гарантировать, что утечки вызвана 2 или 200 причинами. Все что мы знаем, что пользователи сообщают об утечках. Очень часто.
Что это все значит: что одни из лучших разработчиков мира форкнули Ruby специально для использования на мобильных устройствах и получилась система, которая крэшится и теряет память, что и составляет основной набор ошибок памяти. До настоящего момента исправить это им не удалось, хотя они явно стараются из всех сил. И они же утверждают, что «пробовали несколько раз это починить, но хорошего решения (которое сохраняет производительность) не нашли».
Я не утверждаю, что форкнуть JavaScript, чтобы получить приемлемую производительность при работе с памятью — невозможно. Лишь хочу сказать, что похоже это сделать это очень сложно.
Дополнение. Участник проекта Rust:
Я участвую в проекте Rust, целью которого является безопасная работа с памятью без дополнительных буферов (zero-overhead memory safety). Мы поддерживаем GC-объекты через синтаксис "@-обертки" (описание типа будет "@ T" для любого типа T) и столкнулись с проблемой, которая заключается в том, что сборка мусора затрагивает все аспекты языка. Даже если сборка мусора поддерживается опционально, необходимо тщательно проектировать язык, чтобы не тратить лишнее место на обычные указатели (не управляемые сборщиком). Это нетривиальная проблема, не думаю, что ее можно решить, форкнув JavaScript.

ЛАДНО, ЧТО НАСЧЕТ ASM.JS?


ams.js интересен тем, что предоставляет модель JavaScript, но без сборщика мусора. Теоретически, в «правильном» браузере и с правильными API, может и выгорит. Вопрос в том, получим ли мы «правильный браузер».
Mozilla, являясь автором технологии, разумеется легко согласилась и реализация появится уже в этом году. Реакция Хрома более смешанная. Очевидно, что это конкурирует с собственной разработкой Google: Dart и PNaCl. Есть даже баг на этот счет, но один из хакеров V8 его не любит. Что касается Apple, ребята из WebKit хранят молчание. IE? Тут надежды нет.
В любом случае, не очень ясно, отчего этот Единственно Правильный Пофиксенный JavaScript удовлетворит всем просьбам. Даже если он победит — вряд ли это будет JavaScript. В конец концов, он порожден отказом от сборщика мусора. Так что он может быть похож на C/C++ или другой язык с ручным управлением памятью. Но он точно не будет тем динамическим языком, который мы знаем и любим.

МЕДЛЕННЫЙ ПО СРАВНЕНИЮ С ЧЕМ?


Одна из проблем с утверждениями «X медленный» и «X не медленный» заключается в том, что никто не задает точку отсчета. Если вы программируете веб, то для вас «медленный» значит нечто иное, чем для разработчика высокопроизводительного кластера или разработчика встроенных систем. Ну теперь, когда мы прошли по всем ухабам и проделали всевозможные тесты производительности, я назову три точки отсчета, которые полезны и сравнительно корректны.
Если вы — разработчик веб, смотрите на iPhone 4S Nitro как на IE8, их производительность примерно одинакового класса. Это позволит вам примерно представить как написать код. JS следует использовать очень аккуратно, или вы столкнетесь с различными платформо-зависимыми хаками, которые его разгоняют. Некоторые приложения просто не удастся эффективно реализовать, даже в популярных браузерах.
Если вы — разработчик x86 C/C++, смотрите на разработку веб-приложений на iPhone 4S как на C окружение, которое работает в 50 раз медленнее, чем аналогичное на десктопе. На порядок скорость уменьшает архитектура ARM и еще в 5 раз — JavaScript. Или можете взвесить плюсы и минусы разработки в не-JavaScript среде, которая на порядок медленнее декстопа.
Если вы — разработчик Java, Ruby, Python, C# , смотрите на разработку веб-приложений на iPhone 4S следующим образом. Это компьютер, который на порядок медленнее, чем вы ожидаете (потому что ARM) и производительность экспоненциально падает, если использование памяти превышает 35 Мб в любой момент времени, поскольку именно так ведет себя сборщик мусора на этой платформе. Также приложение будет закрыто, если выделение памяти превысит 213 Мб. И никто не даст этой информации на этот счет в момент выполнения, намеренно. Ах, ну да — и люди еще будут просить создавать приложения, которые обрабатывают фото и видео.

ЭТО РЕАЛЬНО БОЛЬШАЯ СТАТЬЯ


Вот что следует помнить:
* в 2013 году JavaScript является слишком медленным, чтобы его можно было использовать на мобильных устройствах для создания приложений типа обработки фотографий и пр.:
— он в 5 раз медленнее нативного кода;
— он сравним с IE8;
— он медленнее, чем x86 C/C++, примерно в 50 раз;
— он медленнее, чем серверные Java/Ruby/Python/C# примерно в 10 раз, если программа выделяет не больше 35 Мб памяти и резко замедляется при дальнейшем выделении памяти;
* Наиболее реальный способ его разогнать — это увеличить производительность процессора до уровня десктопа. Возможно это и произойдет, но не в ближайшем будущем;
* Сам язык последнее время нисколько не ускорился и его разработчики утверждают, что при сохранении языка и API, производительность никогда не догонит нативный код;
* Сборщик мусора сильно тормозит при ограниченном количестве доступной памяти. Он ведет себя сильно хуже, чем на декстопах или серверах;
* Каждый мобильный разработчик, независимо от того — использует он сборку мусора или нет — тщательно продумывает работу с памятью;
* JavaScript в настоящий момент фундаментально против того, чтобы даже попробовать оценить расход памяти на мобильных устройствах;
* Даже если они захотят дать эту возможность, опыт подсказывает, что сделать это будет очень трудно;
* Есть надежда на asm.js, но он, скорее, напоминает C\C++, чем что-то динамическое типа JS.

ПОДНИМЕМ ГРАДУС УРОВЕНЬ ДИСКУССИИ


Не сомневаюсь, что после статьи на меня посыпятся сотни емейлов, которые цитируют одно из вышеприведенных утверждений и не согласны с ним, не приводя никаких доказательств (типа тех, что предоставил я) или утверждая, что «однажды я написал текстовый редактор и он работал нормально» или «есть люди, которых я не знаю, и которые написали симулятор полета и у них не было проблем». Я их буду удалять.
Если мы хотим прогресса в мобильных нативных или веб приложениях, то дискуссия должна опираться хоть на какую-то основу: тесты, цитаты, статьи, что угодно. Хватит уже комментариев типа «однажды написал я веб-приложение и оно работало нормально». Достаточно уже было гадания на тему — правильно ли Facebook выбрал HTML5 или надо было писать нативно.
Задача в том, что определить количественно насколько мобильные веб-приложения и нативная экосистема может стать лучше. Ну и потом что-то с этим сделать.
Автор оригинала: Drew Crawford
Григорий @Krovosos
карма
126,2
рейтинг 0,0
Самое читаемое Разработка

Комментарии (74)

  • +10
    Ух, осилил.
    Короче какими бы быстрыми не были нативные приложения (а это логично, я сам баловался когда-то с PHP-GTK), большинство людей будет писать веб из-за доступности к понимаю, отсутствия необходимости развиваться и себя утруждаться изучением платформы.

    Я лично знаю людей, которые бралить писать приложение для Андройда с использованием позиционирования и графическими отрисовками.
    Начали писать нативное — закончили для веб на JavaScript.

    У всего своя ниша.
  • –4
    в managed языках вызовы функций по умолчанию виртуальны, в то время как в C++ вызовы по умолчанию inline-ятся (от переводчика: то есть тело функции напрямую компилируется в код вместо call/ret)


    Ерунда. Вместо вызова вставляется тело только для самых простых функций. В основном же используются такие же long jmp или call/ret.

    Я думаю, каждый кто собрался писать приложение на основе html/js для мобильной платформы уже заранее знает что оно не будет быстрее нативного кода. Но. На андроиде большинство приложений пишутся на java и точно так же при работе проходят через JIT. Так что они тоже помедленнее нативного кода.
    • +5
      Смотря какой уровень оптимизации, при -O3 gcc и llvm уже вертят функции и методы как им хочется, компилятор обладает полной информацией по сайд эффектам.
      • 0
        Не распространяется на динамические библиотеки, там надо инлайнить ручками. Хотя там может быть LTO поможет, но не всегда.
        • 0
          Пишу под Windows, разницу между dll и exe не вижу в упор: и там и там PE, оба могут собраться из одинаковых объектников. Основное отличие в точке входа: в случае dll это DllMain, а в случае exe это ThreadProc. Наличие/отсутствие таблиц экспорта (exe vs dll) ни на что существенно влиять не может.
          Искренне полагаю, что в случае unix'а или linux'а будет тоже самое: PE от COFF ушёл недалеко.
          Так что же там такого то, что может так смущать компилятор или линкер?
          • +1
            Ну смотри, если у тебя библиотечная функция, то компилятор не имеет доступа к её коду, это же касается просто различных объектников. Соответственно, без включенного LTO он даже не пытается заинлайнить функцию, код которой ему неизвестен.
    • +1
      " На андроиде большинство приложений пишутся на java и точно так же при работе проходят через JIT."

      И это весьма заметно, особенно при первом запуске.
      А игры, сделанные для андроида на яве иногда так безбожно тормозят.
  • +7
    От себя могу добавить про:
    >НО КАК ТОГДА MONO/ANDROID/WINDOWS MOBILE УСПЕШНО СПРАВЛЯЮТСЯ С ЭТИМ?

    при разработке игр (а это наверняка самые требовательные по памяти приложения), разработка идет так, чтоб GC не дергался вообще.
    Для этого все объекты пихаются в пулы, где нужно используются стековые объекты, GC при вызове не имеет что чистить и отрабатывает моментально.
    Освобождается память между уровнями, при лоадинге итд — тогда, когда можно.
    • +2
      В приведенном вами разделе статьи есть ниже цитата:
      Я занимался мобильными играми на Java… Лучший способ избежать сборки мусора (которая будет происходить в произвольный момент времени и убьет производительность игры) — вообще не создавать объекты в основном цикле. Другого пути нет… Только ручное управление объектами, увы. Так делают все основные высокопроизводительные игры на мобильных устройствах.
      • +2
        Между «не создавать объекты» и «создавать объекты так чтоб GC не вызывался» есть разница, согласитесь
    • +3
      Писали же когда-то игры и для J2ME.
      Как-то работало.
      Не сказать чтоб это было приятно и програмеру и пользователю.
      Всё внутри на статиках, в коде куча мест типа while(столько-то времени) System.GC() после загрузки данных уровня и так далее.

      Тут и с нативным кодом часто упираешься в ограничение по памяти.
      Приходится потом тасовать — а давай-те ка мы это пока выгрузим, загрузим то, рассчитаем что нам надо, а потом опять загрузим первое.
      Но тут хоть есть уверенность, что если я сказал — выгрузить сейчас, то это будет проходить именно сейчас, а не когда-то потом, когда проснётся GC.
      Ну и опять же — ретины, размеры текстур и прочего — это всё растёт, съедает туже память что и остальные данные из за этого потребление быстрее растёт, приходится более активно загружать/выгружать данные.
      В нативном коде из за этого могут возникать свои проблемы — как например фрагментация памяти. Но с ней тоже можно вполне успешно и не сложно бороться.
      В играх вообще, не зависимо от нейтив/менеджет стараются память не дёргать. В нейтиве просто больше шансов что без твоего ведома этого не произойдёт.

      PS: Извиняюсь — кажется у меня получился не ответ а какой-то необработанный поток сознания :)
      • +3
        Если в 2х словах, то я хотел сказать: если писать без GC — одни проблемы, если писать с GC другие. Но если программист нормального уровня и знает что делает ни одно ни другое проблемой не является.
  • +4
    Почему в переводе везде APC вместо ARC? Тогда уже АПС :)
    • +6
      Автоматический Расчёт Ссылок :)
    • +1
      Исправил.
  • +2
    Сплю и мечтаю, что когда-нибудь можно будет скрестить быстрый layout-движок(по типу WebKit) с управляемым языком [из среды .Net, к примеру]. Потому что реализовывать интерфейс удобнее на html, а логику — на нормальных языках.
    • +1
      Как-то уже обсуждалось на Хабре, amenshov писал об использовании WebKit .NET:
      … Но основной плюс, на мой взгляд, заключается в том, что можно иметь идентичный интерфейс на десктопе, в мобильном приложение и вебе...
      • +2
        Увы и ах, я капризный разработчик и не хочу тянуть за собой две «операционные системы». Если есть .NET — зачем еще и V8 грузить в память?
        Ну даже если и так, ни один враппер над вебкитом пока не позволяет сделать прозрачной связи между «половинками» приложения.
        • +2
          К двум «операционным системам» добавьте ещё третий фрэймворк типа ExtJS для построения интерфейса :)

          Если у вас есть .NET, и вы пишете только под Windows, то можно, наверное, использовать родной WebView с движком IE, и в нём уже лепить интерфейс. О прозрачной связи я и не думал, как веб-разработчик я представлял себе «клиент-серверную» связь между половинками: интерфейс весь на HTML/JS, тяжёлая логика и хранение данных — в вашем .NET-приложении, а обмен между ними через AJAX. И вообще, я думал скорее о Node.js вместо .NET, чтобы не писать одновременно на двух языках :)
          • 0
            Суть моей задумки, как раз в том, чтобы уменьшить число прослоек между интерфейсом и бизнес-логикой. Не, по такой логике можно делать еще проще: HttpListener для ядра приложения(или IIS Express + ASP.NET Web API) и любой браузер для представления.
            Но это также, как и с Xorg: зачем организовывать сетевую передачу данных на локальном уровне?
    • +1
      Есть много биндингов для webkit. Только принципиального прироста в производительности, к сожалению, нет. На много эффективнее использовать комбинированный подход.
    • 0
      > Потому что реализовывать интерфейс удобнее на html, а логику — на нормальных языках.
      QtQuick, не?
      • –2
        Не, в QT работа с базами данных еще хуже, чем в Windows RT =)
        • +2
          > в QT
          Здесь традиционный комментарий о разнице между Qt (toolkit) и QT (QuickTime).

          > работа с базами данных еще хуже, чем в Windows RT
          Возможно Gorthauer87 сможет что-то подсказать вам, если конечно у вас какие-либо конкретные вопросы по данной теме.
          Со своей ламерской точки зрения разве что скажу, что вот гляжу я на Akonadi, и вроде вполне оно дружит хоть с MySQL, хоть с PostgreSQL.
          • 0
            Дружить с базами данных- это одно, а иметь функциональную ORM — это другое
          • 0
            с SQLками Qt то работает, но без всяких любимых энтерпрайз прогерами orm'ов, плюс баги таки вылезают частенько.
            Впрочем, если человек хочет юзать orm, то он явно не собирается писать быстрое приложение.
    • 0
      Интерфейсы лучше реализовывать на языках специально созданных для этого, нет?
      • 0
        Абсолютно согласен. Но в .NET есть два основных представителя: морально устаревший и тормознутый Windows.Forms(зато очень удобный для разработки, нет, я серьезно) и новоделанный WPF, который по всем характеристикам должен запихивать HTML5 за пояс, но, каким-то неведомым образом, умудряется работать медленнее Internet Explorer'a. Единственное, что мне не нравится в WPF — это отсутствие мало-мальски приемлемых тем для приложений. Есть MahApps.Metro, есть Elysium, есть набор портированных с сервелата «презентационных тем». Вот и все. Нормальное оформление (по типу DevExpress) будет стоить уже денег. А создавать свою тему — это довольно долгий и утомительный геморрой(учитывая все странности WPF). Вот и хочется, чтобы можно было взять «bootstrap-подобную поделку», почистить ее от кроссбраузерности и делать быстрый интерфейс с нормальным дизайном
        • +1
          XAML очень мощный язык, но надо уметь его готовить. Не понимая как идёт Messure всех объектов, легко написать тормознутое приложение. Часто у них всего пару строк в вёрстке поправить и все летает.

          А темы, ну на нормальный дизайн всегда приходится тратиться.
          • 0
            Спрошу тогда у Вас: лушче использовать абсолютные отступы (Windows Forms style) и один большой Grid или все же вложенные «резиновые» макеты через ColumnDefinitions/RowDefinitions?
            // Я понимаю, что ответ очевидный, но интересует мнение другого человека
            • 0
              Не совсем понимаю вопроса. Но чем меньше вложенность и резиновость, тем быстрее будет рисоваться. Если вы возьмете StackPanel (или лучше ListBox) и зададите ItemTemplate у которого высота нефиксированная, то эта конструкция будет рисоваться довольно медленно.
              Общий совет такой: если можете уменьшить вложенность контейнеров — уменьшайте. Если можете зафиксировать размер — фиксируйте.

              Grid — очень мощный контейнер. Разбивать его на три-четыре ряда(колонки), у которого 1 звёздочкой, остальные фиксированные — нормально. Нормально даже, если у ряда высота выставлена в Auto, но есть элемент в ряду, который имеет конкретный размер. Ежели у вас все ряды Auto и контент накидывается динамически, это может вызвать лаги.

              Немного сумбурно рассказал, но если читать в этом направлении, должно стать понятнее msdn.microsoft.com/en-us/library/system.windows.frameworkelement.measureoverride.aspx
              • 0
                Спасибо ра разъяснение. Именно это и хотел узнать
  • +3
    Все так, но имхо asm.js все-таки может стать серебряной пулей. Он разработан, чтобы выполняться быстро; обычный js будет в него компилироваться статически (как скажем Си компилируется в обычные машинные коды).
    • +1
      Один вопрос — зачем JS компилировать в asm.js, если можно его компилировать сразу в нативный код?
      • 0
        Ну и какой именно нативный код мне выбрать, если приложение должно работать и под ИЕ8, и под айфоном, и под чем угодно, причем я даже не знаю всех платформ?
        • –1
          JIT?
    • 0
      > обычный js будет в него компилироваться статически
      Опишите, пожалуйста, как вы себе представляете эту статическую компиляцию кода на динамическом языке. Я себе могу представить разве что скомпилировать в него ещё и GC =)
  • 0
    Я оставлю здесь ссылку, которой сегодня поделился в твиттере bobuk: blog.mozilla.org/javascript/2013/08/01/staring-at-the-sun-dalvik-vs-spidermonkey/

    Это сравнение скоростей кода asm.js (запущенного в Mobile Firefox Nightly), нативного кода и dalvik-кода под андроид. Смотрите сами, кто кого рвёт как тузик грелку.
    • +7
      Я сильно подозреваю, что ценность этих тестов равна нулю. По тем же причинам, о которых упоминает автор статьи. Доводы-то те же самые: да, можно ускорить некоторые места (связанные исключительно с простыми расчетами) с помощью asm.js, но проблемы с памятью-то никуда не деваются. Как и тормоза, связанные с DOM и прочим окружением. Этот как если представить дорогу Москва-Питер со светофорами, пробками, ямами и сказать, что вот на отдельных ( и до этого прямых и быстрых участках) мы улучшили среднюю скорость проезжания с 80 до 100 км/ч. Насколько уменьшится общее время в пути, которое составляет 7 часов? Ну, минут на 15.

      И еще такой момент. Какова собственно цель написания веб-приложений? Кросс-платформенность + «простота» программирования JavaScript. «Простота» с точки зрения мощности, гибкости переделок и прочих плюшек. А теперь представим себе как выглядит приложение, оптимизированное asm.js? Это такой винегрет обычного JavaScript-кода, перемешанного вставками оптимизированных кусков. Вы представляете себе сложность поддержки и развития таких приложений? Ведь это сводит на нет все преимущества JS, уж лучше сразу писать нативно…
      • +1
        Насчет последней фразы: «лучше писать нативно». Оно-то лучше, но для скольки осей?
        В качестве начальных условий — две, iOS и Android. Это абсолютный минимум. А по-хорошему бы желательно добавить WP и BlackBerry. А еще на горизонте маячат всякие убунты, файрфоксы и тайзены — и, неровен час, какая-то их них возьмет, да и взлетит?

        Поддерживать такой зоопарк, имхо, совершенно нереально для небольших и даже средних компаний. Всякие там Facebook, Skype и Instagram — выкрутятся, за них беспокоиться не стоит. А остальным как быть?
        • +1
          Сконцентрировать усилия там, где больше денег :)
          Если у вас простое приложение, с простой веблогикой то html5+js может приемлемо работать.
          Но делать фотофильтры на JS или более-менее сложную игру — вам же геморойнее будет.

          Но с другой стороны можно стать и заложником технологий, что предётся потом как фесбуку переписывать с html5 своё приложение ( да ещё и сделали это так криво, что еле протолкали это в далвик )
          • 0
            Игры — это особая категория. Я не игроман и могу ошибаться, но… Мне кажется, что там сейчас такая конкуренция, что если игра X не выйдет на платформе Y — никто особо и не расстроится. Просто возьмут игру Z и убьют время в ней. Игры взаимозаменяемы (немного утрирую, конечно).

            А вот возьмем для примера приложение-клиент к какому-то сервису/сообществу. Допустим, сервис этот весьма популярен, но к гигантам не относится. И сервис этот в каком-то смысле незаменим для пользователя. Ну например тот же Хабр — если бы ТМ решили выпустить официальный клиент под весь спектр устройств? Подозреваю, что они бы повесились. И как тут выбирать (не)приоритетные платформы?
            • +1
              А то что на каждой платформе свой design guideline и свои требования к UX вы указать забыли? Тут никакой html5\js\etc не спасет — вам придется делать РАЗНЫЙ интерфейс под каждую платформу, и лучший это способ — реализовать на нативных элементах. А логика работы запросто делается кроссплатформенной — пишется на нативном языке C\C++, или выносится в облако в виде SaaS.

              Если заказчик хочет кастомный интерфейс под кучу платформ — пускай оплатит суппорт UX под каждой платформой, а это будет стоить дорого. А если мы играем в демпинг — вот вам и веб приложения, тормоза, не соответствие design\ux guidelines и тд.
        • +1
          Так это, С++ везде поддерживается
          http://msdn.microsoft.com/en-us/library/windowsphone/develop/jj681687(v=vs.105).aspx
          developer.android.com/tools/sdk/ndk/index.html
          stackoverflow.com/questions/8759573/utilizing-c-in-ios-and-mac-os-x-applications
          askubuntu.com/questions/259001/can-i-use-native-the-qt5-c-library-to-create-applications-for-the-phone
          developer.blackberry.com/develop/platform_choice/ndk.html

          Ну да, интерефейс придется, скорее всего, для каждой ОС свой делать. Ну так это в любом случае, иначе выглядеть он будет не очень.
      • 0
        Вы просто не понимаете, что вообще такое есть asm.js.
        Взяли прогу на любом, в принципе, языке, но допустим на обычном js.
        Сунули её в компилятор asm.js, который сделал полный статический анализ программы. На выходе мы получили низкоуровневую кашу, для исполнения которой не нужны динамические типы, не нужна сборка мусора, не нужны классы-обертки, рантайм-проверки и пр. Все, что там есть — это числа, строки, функции и структуры в стиле Си. Соответственно, знающий про asm.js jit-компилятор в браузере может исполнять эту кашу очень быстро, так как отсутствуют все перечисленные в этой статье проблемы с производительностью.
        Но при этом, это все еще javascript, который можно скормить какому-нибудь ИЕ.
        А на этапе отладки вы просто ничего не компилируете и работаете с исходным js-кодом.
        • +2
          Это половина правды. Другая половина заключается в том, что для оптимизации кода он должен удовлетворять определенным ограничениям. В которых собака и зарылась. И которые сводят все преимущества динамического языка на нет.
          • 0
            Так поэтому надо писать на C и компилить код в LLVM а потом в asm.js. По идее получится почти совсем такая же скорость, как если бы просто скомпилили C в нативный код.
        • +1
          >Взяли прогу на любом, в принципе, языке, но допустим на обычном js.
          >Сунули её в компилятор asm.js, который сделал полный статический анализ программы.
          А что по-вашему тогда JIT делает? =) И почему «компилятор asm.js» и «знающий про asm.js jit-компилятор» внезапно будет это лучше делать?
          • 0
            JIT-компилятор сильно ограничен во времени, иначе время «jit-компиляции» будет больше времени исполнения. Поэтому полный анализ кода ему недоступен. Отдельный компилятор же таких ограничений лишен и может спокойно выяснить, например, что такая-то переменная всегда число, или такая-то функция захватывает вот эти две переменные таких-то типов (и их можно ввести в структуру this) и т.п. и т.д.
            • +1
              Статическая компиляция — не панацея. JIT-компилятор может собирать профиль и перекомпиливать метод. JIT-оптимизатор может делать агрессивные оптимизации, делая довольно-таки смелые предположения, и потом перекомпилировать методы, если предположения не оправдались. Статический компилятор вынужден быть консервативным.

              А вот если в asm.js скомпилить какой-нибудь C, то можно и кодек написать, и рендерер.
    • +2
      Ценность этих тестов нулевая. К примеру гениальное объяснение почему деревья на asm.js быстрее нативного
      Notice that binary-trees allocates a lot of objects. In C++, this causes many calls to malloc. When compiled to native code, the malloc implementation occasionally uses expensive system calls to allocate new memory from the system.

      Т.е. они не умеют работать с аллокатором памяти на болшом кол-ве обращений к нему и ругают нативный код? эх, если уж на то пошло — взяли бы 3х лучших инженеров из JS, Native, Dalvik и одни и те же тесты попросили их выполнить максимально быстро — вот тогда еще хоть что-то можно сравнивать, но тогда результаты не понравяться тем кто заказывает такие «красивые графики».

      • 0
        Т.к. в asm.js нет управления памяти вообще (а есть только возможность писать в заранее выделенные байтовые массивы), то, по сути, нужно его ещё написать в виде библиотеки. Подозреваю, что где-то он там и зарыт — то ли свой написан для конкретного трансформера в asm.js, то ли трансформер берёт какой-нибудь LLVM-код стандартной библиотеки C и оттуда просто вкомпиливает в программу код malloc и free. Почему в случае asm.js получилось даже быстрее — вопрос отдельный.
  • +11
    Почему веб-приложения на мобильных платформах работают медленно
    Потому, что весь стек веб технологий тормозное говно?!
    • +2
      В статье уже есть момент про клуб тавтологии.
    • +3
      ну зачем сразу про стек…
      момоему если мы апплик(флэш или джава) запускаем в браузере(хром например) который запущен в ОС(Убунту например) запущенную понятное дело на какойто аппаратной платформе имеем тройное заложение уровней трансляции комманд как минимум.Если есть по дороге скачки битности — то только хуже становится. Плюс геморрой с засёром памяти.
      Это как три шестеренки подряд. Потери всегда будут и КПД никогда небудет в 100%. То что в лучае описанного стека шестеренки кривые и весьма параноидальной формы — только ухудшает и без того небезоблачную ситуацию.
  • 0
    Большая проблема производительности JS, как мне кажется, не сборка мусора, а динамический диспатч методов. Чтобы вызвать метод/функцию, нужно взять объект, взять его внутреннюю хэш-таблицу с отображением атрибутов, произвести там поиск по строке (типа strcmp), потом уже вызвать код. И закешировать ничего нормально нельзя, так как атрибуты объекта теоретически могут измениться в любое время, таково уж свойство динамических языков. И так повторяется много раз. В других языках (C++, Java, да и C# наверное) диспатч происходит не по строке, а по некоему указателю/хэндлеру, которых может уместиться в int (в крайнем случае в long). Разные оптимизации и JIT в JS могут помогать, но не сильно.
    • +2
      А вот это неправда. Все современные JS-движки строят внутри себя скрытые классы, которые автоматом генерируются, когда в какому-то объекту в рантайме добавляется свойство. А дальше применяется целая куча оптимизаций, которые позволяют свести вызовы к virtual table, а иногда вообще к абсолютным call'ам или даже заинлайнить.
  • +2
    Аналогия из мира приложений, работающими с СУБД: автор приложения, разрабатывая его, часто работает с плохо отстроенным СУБД, думая при этом, что для отладки хватает, а в реальном мире администратор настроит сервера как надо. Администратор при работе с ПО, в свою очередь, рассуждает, что приложение писали под решение задач, раз не ругается на СУБД, значит, с ней все нормально. И обе стороны (ПО и СУБД) функционируют неоптимально, поскольку программист не знает, что рекомендовать для настройки СУБД (он не спец по ней), а администратор при внедрении всего комплекса может полагаться только на свои догадки.

    С JS та же история: те, кто пишут движки в браузерах, для понимания прогресса в работе используют бенчмарки. В жизни эти сферические попугаи не дают ответа на вопрос, быстро ли в браузере будет работать то или иное веб-приложение, но тестировать браузер работой конкретной веб-разработки как-то несколько однобоко. Опять полагаемся на везение и то, что браузер «подойдет» к веб-приложению. Жуть!
  • 0
    Отличная статья, только примеры чуть-чуть неудачные имхо: как раз пример с фотографиями/кадрами и сборщиком мусора мне кажется притянутым за уши. Я не совсем в теме мобильных приложений и графики, но подозреваю, что куча больших буферов одинакового размера будет примерно одинаково хорошо/плохо в плане производительности и оверхеда работать как с malloc/free, так и со сборщиком мусора. Malloc/free или, что примерно то же самое, ARC тоже не идеальны и возможно даже чуть более подвержены фрагментации, с которой надо отдельно бороться.

    Равно как и алгоритмы обработки картинок (например, автоматическая яркость/контраст или резкость для фотографии), как мне кажется, как раз можно дооптимизировать до сравнимого с нативной производительностью (если не рассматривать рукописный код на ассемблере с SIMD), например с помощью упомянутого asm.js. Там ведь в идеале даже от оверхеда «виртуальных функций» можно избавиться, правильно пиля код на функции.

    Но оба тезиса сразу теряют силу как только мы начинаем говорить о других видах приложений, не занимающихся хранением и обработкой больших монолитных кусков данных.
  • +4
    Скорость JS-кода совсем не важна. Главная проблема — отзывчивость интерфейса.

    Если интерфейс будет быстр, а во время обработки будет показываться честный прогресс-бар, то пользователь не заметит проблем. Это как хаотичная очередь в старом Сбербанке и современная с номером на бумажке — честная и предсказуемая может работать медленнее хаотичной, но всё равно больше понравится пользователю.

    То есть я согласен, что проблема с производительностью веб-приложений есть. Но она совершенно в другом месте — в CSS, отрисовке, фреймах. А не в сухой математике тестов. Например, хорошие анимации работают только через CSS Animation или CSS Transition — они даже не вызывают JS-кода. Но именно лаги анимации создают ощущение медленного интерфейса.
    • 0
      Но если выключить JS скрипты то большинство сайтов работает быстрее и отзывчивее. Видимо еще дело в том что браузер на каждый скрол зачем-то обрабатывает какие-то действия через скрипты.
      • 0
        Проверяет нет ли обработчиков как минимум.
  • 0
    Если C-программа выполняется за 10 мс, то 50 мс программа на JS может считаться «сравнимой». Но если C-приложение занимает 10 сек, то 50 сек JS приложение для большинства уже несравнимо с ним.


    Как по мне, то не очень удачный пример. Несравнимы, по-моему, цифры порядка 1-3 секунды и 5-15. Меньше нескольких секунд особо будут незаметны, особенно при наличии реалистичных прогресс-баров, а начиная с порядка 10 секунд «заставляют» убирать фокус внимания с приложения.

    А вообще основная проблема по моему дилетантскому мнению в динамической типизации. Особенно при работе с массивами (вернее хэш-таблицами) и объектами. В языках со статической типизацией транслятор заранее знает сколько памяти нужно выделить под сущность и размер этого куска памяти ему в дальнейшем изменять не приходится. Для JS же характерно создание элементов сущностей по мере необходимости, то есть постоянный вызов где-то внутри движка realloc или его аналогов. Возможно проблему хотя бы частично решит некое подмножество JS или просто следование соглашениям типа «сначала объявляется всё что нужно и лишь потом используется», «руками разбивать циклические ссылки» и «не менять тип сущностей во время исполнения». Ну и ручное управление gc (разрешить, запретить, запустить цикл очистки) наверное не сложно ввести.
  • +8
    Респект переводчику. Как представлю, сколько времени и сил надо было потратить на это…
  • +3
    При этом количество вакансий на js-разработчиков, умеющих мобильный веб — растут как на дрожжах.
    • +1
      Но платят этим разработчикам меньше, чем разработчикам нативных приложений. Всё нормально.
  • 0
    Эм PoP применять стали совсем не давно и если посмотреть многие устройства имеют отдельные модули памяти не на процессоре, так что как минимум один слабый аргумент я вижу.
  • 0
    JavaScript изначально не предназначался для создания мобильных приложений.
    В целом — www.youtube.com/watch?v=clKi92j6eLE
  • 0
    Давно об этом спрашивал — зачем писать на js игры, приложения и даже операционные системы, если оно всё тормозит…
    Отличная статья!
  • +2
    Не хотел бы никого обидеть, но что то мне подсказывает, что без целенаправленного возможно заказного пиара нативного программирования в этой статье не обошлось.
    Потому как
    1) тут одностороннее утверждение — «только так и не иначе»
    2) свидетельства «инженеров» взятые из желтой прессы. Где ссылки на конкретные исследования проведенные над архитектурой процессоров и зависимость от этого их скоростей??? — нет такого, т к научная литература намного недоступнее чем желтая пресса. А в наше время почему-то вошло в моду цитировать распиаренные «догматы» из желтой прессы, безоговорочно уповая на их истину.
    3) Почему желтая пресса это плохо и в нее нельзя верить? Потому как желтая пресса в наше время — это инструмент пропаганды, который настроен на формирование определенного мнения в массах.
    4) Почему выгодно пиарить нативное программирование? Да потому что, один из столпов на чем держится современная экономика и в частности IT индустрия — это РАЗДЕЛЕНИЕ ТРУДА. Поэтому не выгодно когда есть «фока на все руки дока», который может самостоятельно сварганить к примеру сайт на пхп по сути не хуже чем его делают целые студии за цены в десятки раз большие и усилиями трудочасами также большими в разы а то и в десятки. Потому нужно как-то заставить денежную массу крутиться более мощным потоком, повысив себистоимость разработки и количество вовлеченного персонала — сливки то увеличиваются в разы для тех кто сидит сверху. Так и тут чтобы сделать нативное мультиплатформенное приложение — нужно условно сказать «под каждый девайс свой язык».
    5) Ну и еще — рискую изменением рейтинга в не лучшую сторону, что гоню на желтую прессу и современные столпы экономики, но это и будет плюсик в пользу того что мысль моя истинна.
    • 0
      Потому что у каждой мобильной платформы свои гайдлайны. Нельзя портировать одно приложение на все платформы, получится говно. Исключением из правила являются игры.
      Я уже молчу о тормознутости JS.

Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.