company_banner

От черного прямоугольника в Яндекс.Браузере к ускорению всего Chromium

    Сегодня мы расскажем вам историю об одном интересном баге в Яндекс.Браузере, исправление которого привело к значительному ускорению отрисовки во всем проекте Chromium. И помогут мне в этом Кирилл drBasic Плешивцев и Вадим Lof Петров, специалисты из нашей команды, которым и посчастливилось разбираться с проблемой. Передаю им слово.



    Один не совсем обычный баг

    Меня зовут Кирилл, я работаю в группе внутренних компонентов Яндекс.Браузера в Новосибирске. В один не совсем прекрасный день коллеги из тестирования Яндекс.Браузера воспроизвели проблему с проигрыванием видео через Flash Player. И поскольку именно наша группа отвечает за эту часть браузера (медиа, кодеки, вот это все), задача досталась мне. Баг, скажем так, не претендовал на оригинальность. Клик по кнопке Play приводил к черному прямоугольнику вместо корректного воспроизведения видео. Этот симптом я встречал и раньше, поэтому рассчитывал на достаточно быструю локализацию проблемы. Но я ошибался.

    Буквально в первые же минуты удалось выяснить, что черный прямоугольник возникает не всегда, а только для flash-элементов с типом transparent, т.е. полупрозрачных. Отлично, уже есть за что зацепиться при отладке. Собираю debug-версию браузера, запускаю, бага нет. А это уже тревожный звонок. Расхождения в работе debug и release версий — это всегда очень весело. Поэтому решаюсь собрать еще и релизную версию. Собрал, запускаю, бага нет.

    Задумался. В чем отличия моей релизной сборки от той, что собирает сервер? Сходу вспомнил про компоновку библиотек. Разработчики собирают браузер в режиме shared_library. Это увеличивает количество dll, но зато сильно экономит время компоновки. Распространяется же браузер, собранный в режиме static_library, при котором собирается лишь несколько больших dll. Выставляю флаг static_library, делаю полную сборку. Наблюдаю, как link.exe медленно съедает всю оперативную память, но нет, 16 ГБ RAM хватит всем, компоновка завершается без допинга в виде файла подкачки. Запускаю. Бага нет.

    Серьезно задумался. Вспомнил, что сборочный сервер собирает релизный Яндекс.Браузер с флагом official, который немного меняет поведение (подробнее расскажем чуть позже). Собираю с этим флагом. Дрожащей рукой запускаю браузер. Вы уже угадали? Бага нет.

    Тут я не на шутку встревожился и начал думать изо всех сил. Через некоторое время обратил внимание на то, что сервер собирает Яндекс.Браузер с помощью Visual Studio 2013. А я же использовал 2015 версию. Собираю в 2013 версии. Запускаю. Баг есть! Кто бы мог подумать, что я так буду радоваться ошибке.

    Если вы сейчас подумали, что вся проблема заключалась только в версии VS, то ошибаетесь. Баг действительно не воспроизводился в debug-версии браузера. Опытным путем удалось установить, что для появления ошибки с черным прямоугольником браузер должен быть собран не только с помощью VS 2013, но и в статичной компоновке с флагом official. О причинах такого странного поведения вы узнаете чуть позже.

    Следующие два дня были не менее интересными. В ходе отладки мне удалось понять, что сам плагин Flash Player отрабатывает свою задачу корректно: видео воспроизводится. Его интеграция с браузером вопросов также не вызывала. Результат его работы передавался для отрисовки, но по каким-то причинам на экране мы видели совсем другое. А это значит, что баг нужно было искать в той части браузера, которая отвечает за рендеринг. И здесь я передаю слово Вадиму.

    Оптимизируй это

    Как вы уже поняли, теперь на связи Вадим. Работаю я в группе разработки рендеринг-движка Яндекс.Браузера в Москве. Несколько слов о том, как вообще происходит отрисовка в Яндекс.Браузере или Chromium. Все, что вы видите в окне браузера, есть результат совмещения различных слоев (веб-страница, интерфейс), почти как в Photoshop. За работу с этими слоями отвечает компонент Compositor (или Chrome's Compositor == CC). А вот для отрисовки уже каждого слоя СС вызывает специальную опенсорсную библиотеку Skia.

    Вместе с Кириллом мы поняли, что следы бага уходят в Skia. Оставалось понять, куда именно. К счастью, у меня была ценная подсказка. Почти в самом начале Кирилл её упомянул. Речь о том, что проблема возникает только в случае flash-элементов с прозрачностью. Чтобы отрисовать на экране такой элементы, браузеру необходимо совместить картинку видео с фоном. И для этого в Skia есть специальная функция SrcATop, отвечающая за блендинг. Несколько минут поиска, и вот я уже нашел багрепорт со схожей проблемой в Chrome, который окончательно развеял все сомнения.

    Ура. Мы локализовали источник проблемы вплоть до конкретной функции. А теперь, внимание. Этот участок кода не содержал никаких ошибок. Совсем никаких. И работал он идеально для любых сборок кроме самой финальной, которая и отправляется пользователям. Причем только для Visual Studio 2013. И вот в этот момент я понял, почему Кирилл называл этот баг «веселым».

    Начинаю разбираться в прочих отличиях, которые влияют на воспроизводимость ошибки. Пришло время вновь вспомнить про флаг official, который используются только в самом конце. Помимо всего прочего, этот флаг влияет на оптимизацию, а точнее на параметр /LTCG. Когда он указан, компилятор производит достаточно серьезную оптимизацию всей программы. На это уходит очень много времени, поэтому такие сборки просто так не собирают. Но как оптимизация может привести к ошибке? Чтобы понять это, нам понадобится небольшой флешбек.



    В 2011 году проект Chromium стал настолько большим и сложным, что компоновщик Visual Studio 2010 однажды не смог слинковать его со всеми оптимизациями из-за нехватки ресурсов. Чтобы обойти проблему, разработчики решили по умолчанию оптимизировать все подпроекты (а их больше тысячи) не по скорости работы (/O2), а по размеру кода (/O1). И лишь для избранных и наиболее критичных, или для тех, чьи владельцы не проспали эту ситуацию, включили обратно /O2. Например, это сделали для CC и Skia. Вот только в 2013 году при рефакторинге в Skia оптимизацию случайно потеряли. И никто бы ничего не заметил, если бы еще через два года не случился еще один рефакторинг в Chromium, в результате которого часть кода сделали шаблонным и перенесли в header. И вот тут-то все и началось.

    А началось вот что. Когда происходит сборка релизного браузера с флагом official, библиотеки, имеющие разные цели для оптимизации (по скорости, по размеру), оказываются в одной dll. Само по себе это не признак чего-то плохого. Например, в Visual Studio 2015 никаких проблем это не вызывает. Студия пыхтит час над оптимизацией и выдает вполне рабочий код. Но стоит нам заменить её 2013-й версией, и все ломается. Почему?

    Функция SrcATop, которая отвечает за блендинг в Skia, принимает два параметра через регистры xmm0 и xmm1. И почти всегда она работает корректно. Но как мне удалось выяснить в ходе отладки, стоит добавить сюда VS 2013 и непростую оптимизацию, и функция вырождается до такой степени, что начинает возвращать в ответ содержимое первого регистра. Отсюда и появлялся неизменный черный фон вместо видео. Всему виной была неправильная кодогенерация в VS 2013.

    Ускоряем веб-сёрфинг

    При большом желании баг можно было «исправить» локально, немного подкрутив SrcATop. Но мне показалось неправильным, что у такого важного для отрисовки компонента, как Skia, отсутствует оптимизация по скорости. Поэтому я собрал новую сборку, в которой выставил для Skia оптимизацию по скорости. Баг, конечно же, пропал. Казалось бы, можно закрывать задачу и идти пить чай, но нет. Мне нужно было сделать еще кое-что.

    Команда Яндекс.Браузера участвует в разработке Chromium уже не первый год. И это касается не только исправления ошибок. В свое время коллеги помогли с реализацией server push для HTTP/2 и со сборочной системой проекта для Windows. Поэтому и в этот раз я предложил решение проблемы и отправил на рассмотрение готовый коммит, который после небольшого обсуждения был принят.

    Разработчикам из Chromium, так же как и мне, было интересно взглянуть на изменения в плане производительности браузера. Поэтому они прогнали целый комплекс performance-тестов. Практически все показатели для Windows подросли. Часть низкоуровневых тестов и вовсе показала улучшения в 2-3 раза. Интегральный FPS-тест для ключевых сайтов (иными словами, повседневный веб-сёрфинг) вырос на 6,5%. Отзывчивость на ввод улучшилась на 20-30%. В проекте Chromium далеко не каждый день случается оптимизация подобного уровня.



    Учитывая, что VS 2010 уже давно не используется, я предложил попробовать включить оптимизацию по скорости для всего проекта. Тем более что обычная release-сборка (без флага official) всегда оптимизировалась по скорости целиком, и с тестированием никогда проблем не было. Но это уже совсем другая история.

    P.S. Эта отдельно взятая проблема затронула лишь два офиса. На самом деле разработка Яндекс.Браузера ведется не только в Москве и Новосибирске. У нас есть команды еще и в Санкт-Петербурге, Екатеринбурге, Нижнем Новгороде, Минске и Киеве. И если вам было бы интересно к ним присоединиться, то заглядывайте на yandex.ru/jobs.
    Яндекс
    442.39
    Как мы делаем Яндекс
    Share post

    Comments 49

      0
      Если бага была в отрисовке слоев, то почему она не проявлялась в отрисовке других полупрозрачных компонентов?
        0
        Это уже вопрос к msvc2013, как я понял.
          +8
          Не совсем понятно про какие компоненты идет речь.
          Но если предположить, что про что-то простое типа css свойства opacity, то тут надо немного рассказать как хром рисует веб-странички.
          Предположим, что у вас есть страничка с двумя статичными полупрозрачными изображениями.
          Хром ( а реально WebKit ) заранее просчитает финальную картинку, которая получается при смешивании этих двух статичных картинок и браузер уже нарисует финальную картинку.
          Однако, если что-то поменяется, надо будет перерисовать опять всю финальную картинку.
          Чтобы этого избежать, придумали слои. JS разработчики могут на это явно влиять через will-change свойство css. А раньше для этого часто использовали transform z = 0.
          В этом случае, WebKit отдаст браузеру две картинки, и он их смешает сам. С помощью skia.
          В первом случае бага не будет. Потому что там будет использован код WebKit'а.
          Во втором случае баг может быть.

          А еще может все дело в том, что использовался не подверженный багу тип смешивания. Некоторые работали нормально :)
            –4
            На самом деле уже не webkit а blink.
              +1
              Вы код хорошо изучили, прежде чем такое писать?
                +2
                не читая код, можно объяснить, почему все ссылаются на WebKit, если форк произошел почти 3 года назад?
                  +1
                  Думаю потому, что большая часть кода там всё-равно осталась вебкитовская.
                  0
                  Ну и ответьте мне, с какого момента ссылаться на blink а не webkit? когда они добавят фичу X? по мне так когда форк произошел тогда и надо ссылаться на конкретный движок, а не плодить невежество.
            +17
            Если бы Агата Кристи жила в наше время, она писала бы такие детективы.
              +2
              В какой версии хрома этот фикс есть?
                +2
                Фикс попал в Chromium 47.
                  +3
                  Товарищи! Как раз начиная с 47, в Chromium сломан (по крайней мере на OS X) прозрачный режим, то есть рендеринг в прозрачном окне (alphaEnabled). Он используется в nwjs, Electron и Chrome Apps для создания прозрачных окон. Пример глючного рендеринга есть здесь: github.com/atom/electron/issues/4157
                  Так вот, может это вы нечаянно сломали? Мы на работе страдаем очень из-за этого.
                    0
                    Это только Windows зацепило, не?
                      0
                      Возможно. Может ребята в курсе еще каких-нибудь изменений, связанных с этим всем.
                      +3
                      Нет, OS X не трогали, потому что там нет Visual Studio. Но баги в Chromium появляются и так регулярно, поэтому скорее совпадение.
                  0
                  Пока читал в голове то и дело мелькали мысли "Отлично, старый добрый Хабр, статья с деталями и сюжетом, интересно", и это правда, но концовка немного подкачала — по сути всего лишь включили оптимизацию и всем похорошело.

                  P.S. А то, что в 2013 году имея такие бенчмарки взяли и прошляпили — это, безусловно, странно!
                  P.S.S. Невольно порадовался на тему того, что "твой самый странный баг все равно не переплюнули" :)
                    0
                    Станет ли мак-версия быстрее, как и виндовая?
                      +1
                      Нет, это только Windows зацепило.
                      +1
                      Есть ли ещё простор для ускорения? Например вручную переписать на Asm этот кусок кода, добавить SSE2-4/AVX, как это делают в x264 например. Попробовать скопимилировать с помощью Intel C Compiler.
                      0
                      Чтобы обойти проблему, разработчики решили пожертвовать скоростью? Это как понимать?
                        0
                        Проект стал большим, с оптимизациями компилятор справляться перестал, простого решения не нашлось. Чтобы собрать, оптимизацию частично отключили. Немного более медленный, но рабочий код, всегда лучше несобирающегося вообще. Как-то так.
                          +3
                          На самом деле, у них есть аргументы за оптимизацию по размеру кода.
                          1) Размер бинарника увеличивается на 25%, а значит пользователям больше качать.
                          2) Меньший по размеру код более cache-friendly.
                          В общем, они пока решили все не оптимизировать по скорости. Можешь с ними поспорить вот тут https://bugs.chromium.org/p/chromium/issues/detail?id=548457 :)
                          –2
                          Как бы было замечательно если бы был оффис в Алмате!!!
                            +1
                            Верно ли, что слои объединяются софтварно (не GPU)?
                              +1
                              Корректный ответ — когда как. В данном случае — да.
                              +1
                              Если не секрет, расскажите почему разработчики на своей локальной машине уже используют VC++2015
                              а на билд-сервере сборка релиза идет на VC++ 2013?
                                0
                                Chromium тоже постепенно переходит на 2015 версию.
                                  0
                                  Вот, например, изза таких ошибок. Можно собирать и в 2010, а писать в 2015.
                                  Если у вас большая кодобаза, то оттестировать все на уровне чуть ли каждого байта — очень тяжелая задача.
                                    0

                                    Это более вероятно есть баг/недочет в студии нежели ошибка при написании кода. Так что сомнительное занятие искать ТАКИЕ баги. Так, например, на днях не удалось собрать рабочий проект 13ой студией. Проект использует nlohmann/json, но из-за неполноценности реализации 11 стандарта C++ оно просто не компилируется.

                                  0
                                  Такой пространный вопрос и немножко не в тему, но может знаете, почему на Mac (со встроенной графикой) Chrome работает заметно менее плавно, чем родной Safari? Что за хитрые оптимизации заставляют работать дефолтный браузер так быстро и непрожорливо (хотя насчёт последнего начиная с какой-то версии Chrome уже не так актуально)?
                                    +3
                                    Боюсь, chrome всегда будет отставать от safari. Дело в том, что chrome имеет универсальную архитектуру, которая работает на множестве платформ.
                                    Safari же заточен под OS X. Не даром они не смогли поддерживать его под Windows и закрыли проект.
                                    Но есть одна настройка, которая увеличивает плавность скроллинга в chrome. Надо зайти на chrome://flags и включить GPU rasterization для всех слоев:

                                    image
                                      0
                                      Спасибо, попробую!
                                        +1
                                        Chrome Версия 48.0.2564.116 (64-bit), MBP MD313, El Capitan
                                        После включения стала тормозить прокрутка ленты ВКонтакте
                                      +1
                                      Я сейчас схлопочу минусов, но у меня остались вопросы:
                                      Всему виной была неправильная кодогенерация в VS 2013.
                                      Что, и всё? А подробности про вырождение той ошибочно-переоптимизированной функции? А вы писали разработчикам VS 2013 о проблеме? Уверены, что в VS 2015 бага уже нет? А может он только у вас перестал проявляться?

                                      Вообще, краткое содержание статьи мне показалось вот таким: "Мы искали баг в коде, но оказалось, что у VS 2013 кривой компилятор. А ещё мы выставили /O2 и порадовались, что скорость возросла. Конец."
                                        0
                                        ещё "Приходите к нам работать"
                                          +3
                                          Почему VS ведёт себя именно так, ответить смогут только её разработчики. Баг с неправильной кодогенерацией функции SrcATop при разных целях для оптимизации был исправлен в 2015, потому что он пропал не только у нас, но и для всего Chromium (в том числе для иных ситуаций с блендингом).
                                          • UFO just landed and posted this here
                                            +1
                                            Ребята, сделайте уже в Linux версии поддержку H.264. Chrome (из коробки), Vivaldi (с помощью доп.модулей) позволяют смотреть. А я, как дурак, пользуюсь ЯБ, а видео смотрю в другом. Служба поддержки только следующими релизами кормят.
                                            Может хоть тут до разработчиков достучусь?
                                            На всякий случай Ticket#15121617250381325.
                                              +1
                                              Спрошу у коллег. Спасибо за номер тикета.
                                              +1
                                              А когда окончательно откажутся от флеша и закопают его подальше?
                                                +1
                                                Сразу после вендекапца, видимо.
                                                  0

                                                  К 2020 поддержку Flash планируют прекратить. Через несколько лет и из браузеров выпилят.

                                                  +1
                                                  Интересно, а кто-нибудь пробовал собирать при помощи gcc под windows? Насколько браузер хуже/лучше работать начинает?
                                                    0
                                                    Как избавиться от красного цвета в заголовке yandex browser-а? Отвлекает внимание. Беглым взглядом ощущается, как оповещение о важном событии.
                                                    image
                                                      +1
                                                      Могу ошибаться — но кажется, что там действительно какое-то уведомление должно быть, либо касающееся плагина, либо какой-нибудь настройки.
                                                        0
                                                        Как это уведомление увидеть? И главное как соощить браузеру, что это уведомление уже просмотрено и красный цвет можно убрать.
                                                          +1
                                                          Насколько помню — щелкнуть по меню, и в нем они будут. А если это не поможет — напишите в саппорт. Я несколько раз писал — отвечают обычно от дня до 3.

                                                    Only users with full accounts can post comments. Log in, please.