Comments 36
Хорошая статья.
А мужики то не знали!
https://habr.com/ru/companies/servermall/articles/849382/comments/#comment_27400566
вот так вот выглядят флаги компиляции для включения оптимизации с SSE:
#ifdef __SSE__
__m128
В данном случае флаги не нужны. SSE2 — обязательное расширение для 64-битного x86, а NEON для 64-битного ARM.
Ба, да это же косинусное преобразование!
только сейчас обратил внимание - вы написали оптимизацию относительно цветовых компонент, а у нас в Интеле была оптимизация того же DCT с использованием SSE2 (хотя местами исходный SSE и MMX-ы тоже были полезны) относительно сложений по одной компоненте, там есть специальный алгоритм вычисления DCT через векторизацию и он был полностью в целочисленной арифметике.
Я к тому что никакие флаги оптимизации не помогут выбрать нужный вам способ реализации вычислений с использованием инструкций расширения процессора для векторизации. Компилятор не может переписать код функции как вы это сделали.
была оптимизация относительно сложений по одной компоненте
Можно чуть подробнее, что вы имеете в виду?
никакие флаги оптимизации не помогут выбрать нужный вам способ реализации
Так я и не использовал никакие флаги, это же ваш комментарий был.
там же косинусы повторяются, поэтому получается формула для суммы специальная, плюс сложения делаются в виде дерева, то есть складываем попарно, потом в два раза меньше результатов попарно... Там получается гораздо меньше вычислений, но их тоже выписывали без цикла для заданной размерности квадрата пикселей, но я сам их конечно не выводил эти формулы, мне их просто выдали как теорию, поэтому где то они должны быть - искать надо в этом направлении. Это было 20+ лет назад я их конечно не помню, но узнаю с первого взгляда :)!
А про флаги... Так это была ирония, это же:
#ifdef __SSE__
не флаг.
Можно чуть подробнее, что вы имеете в виду?
Скорее всего речь идёт о binDCT
Писал бы автор на C++, мог бы таблицу sRGBToLinear_cache
во времени компиляции через constexpr функцию посчитать.
Вот кто атмосферу топит неоптимизированным кодом)) Спасибо автору!
Работа проделана хорошая, но вот сам blurhash, как по мне, бесполезен и нелеп.
Осталось понять, зачем это нужно. Фронтендерам, которые показывают блюр и подгружают исходник по скроллу, нужно отрывать руки (к Хабру тоже относится).
Чтобы показывать блюр, пока исходник не подгрузился. Не по скролу, а просто
А как правильно нужно делать? Поделитесь опытом.
Везде говорят о необходимости и целесообразности ленивой загрузки изображений - и это справедливо: ибо нафига грузить все разом.
На месте незагруженного изображения что-то ставить или оставлять дефолтную рамку с корявой иконкой? Учитывать ли какие должны быть итоговые размеры изображения или не учитывать?
Если говорить о плэйсхолдере. Кто-то говорит, что не нужен, другие говорят, что он нужен для того, чтобы показать, что изображение появится в этом месте и оно загружается.
А какой этот плэйсхолдер должен быть? Почему в виде градиента это плохо, а в другом виде - хорошо.
Progressive JPEG?
Да, хорошее замечание: нафиг градиент нужен, если есть progressive jpeg. Однако, такой формат файла это не универсальное решение. Есть другие форматы.
Идея blurhash именно в том, что он доставляется мгновенно, вместе с разметкой страницы или данными в json приложения. А progressive JPEG все же требует отдельного запроса. Плюс если изображений много, они скорее встанут в очередь, чем каждое успеет загрузиться на 10% или сколько нужно для генерации первого тира.
они скорее встанут в очередь, чем каждое успеет загрузиться на 10% или сколько нужно для генерации первого тира.
Надо 10-15%, чтобы браузер начал отрисовывать jpeg (он ждёт DC-коэффициенты всех каналов; можно отрисовывать ч/б раньше, но в браузерах так не принято),
В идеале нужна параллельная загрузка 10-15% всех картинок, Cloudflare в 2019 году пишет[1][2], что HTTP/2 что-то такое позволяет (с оговоркой про хром и поддержку серверами...).
Ещё можно запросить только 10-15% (stackoverflow о range requests и прочих костылях), а потом... загружать заново целиком? Получается слишком костыльное и тяжёлое решение.
С тем, что нужно делать ленивую загрузку никто не спорит. Естественно, итоговый размер изображения должен быть заранее известен. А вот нужен ли этот блюр на месте будущего изображения - большой вопрос. Мне кажется, его основное назначение - убрать несколько десятых секунды от Largest Contentful Paint и соответственно получить немного больше попугаев в Google PageSpeed.
Я сторонник того, чтобы вставлять LQIP малого разрешения (15-20px), закодированный в base64.
Да, он будет весить побольше, чем blurhash, обычно порядка килобайта. Но зато он даёт превью сносного качества, по которому действительно можно примерно понять содержание картинки. По blurhash ничего понять невозможно, он выглядит просто как рандомные цветные пятна.
Добавим сюда ещё тормоза (отрисовка JS-ом против нативного кода) и лишние манипуляции с домом (отрисовка на канвасе, а не картинкой).
Я не понимаю нафига нужен blurhash. Реально это карго-культ от программистов, которые слышали про ленивую загрузку, вложили в библиотеку много кода и математики, но не поняли зачем и по дороге расплескали всю пользу для юзера.
Что-то похожее на blurhash можно увидеть на ютюбе - рамка вокруг плеера. Довольно интересный эффект (как фоновая подсветка телевизора). Юзать как плейсхолдер - это на оценку дизайнерам уже.
Да, есть такая фича, "ambient mode / профессиональное освещение" - градиент вокруг видео от почти серого (с оттенком) к чёрному с медленной анимацией. Но связи с blurhash ведь нет.
В Firefox сломана целиком (бандинг в виде бегающих полос), в хроме - частично (без проблем в динамике, но бандинг остаётся). На квадратных и вертикальных видео проблемы заметнее.
Хозяйке на заметку: такой параметр при необходимости позволяет за бесплатно получить «кроп» изображения. Для этого нужно лишь сместить начальный указатель на первый пиксель кропнутого изображения и указать
bytes_per_row
соответствующий не кропнутому.
Не просто бесплатно, потом еще и CVE могут дать! (Стоит помнить, что данные при этом не исчезают, а вот пользователь может ожидать, что после кропа за край уже не заглянуть.)
Было бы интересно почитать про саму математику и как эта штука работает. Там есть описание, но что-то мне знаний не хватило для понимания.
А ещё лучше выбрать альтернативу с альфа каналом, blur hash без него к сожалению.
float *sRGBToLinear_cache = NULL;
static void init_sRGBToLinear_cache() {
if (sRGBToLinear_cache != NULL) {
return;
}
А как у этой штуки дела со thread safety? На первый взгляд - параллельный вызов может начать читать из неполностью инициализированного кэша и получить кривой результат в итоге.
Спасибо, очень дельное замечание. А как бы вы обезопасили код? Навскидку: можно присваивать поинтер только когда массив подготовлен. Да, другой поток сможет подготовить второй массив и в редких случаях займёт память второй раз, но кажется что это небольшая проблема.
Традиционно - лок критической секции, еще перед проверкой указателя на NULL. Например, завернуть весь вызов init_sRGBToLinear_cache
в критическую секцию.
Ваш вариант - да, может несколько раз выделить память (причем не обязательно только два раза - смотря сколько паралелльных запросов прилетит на старте)
Вы очень постарались сделать статью с мемами и рассуждениями на Хабре, и почему стоит внедрить эти изменения - выгодно, но в самом PR кинули в лицо таблицы бенчмарков без особых пояснений. Я понимаю, что код - это тоже язык, но его нужно сначала прочитать, потом понять - это время. Вы ведь хотите это время для разработчиков сократить?
Я написал Дагу непосредственно в пм слаке, завтра проверю ответ, т.к. таймзона.
в самом PR кинули в лицо таблицы бенчмарков без особых пояснений.
Вы, извините, какую-то ерунду эмоциональную пишете. «Ты прислал Pull Request, но сделал это без уважения», ага. В PR, преследующем цели оптимизации, бенчмарки это самое важное.
Я понимаю, что код - это тоже язык, но его нужно сначала прочитать, потом понять - это время. Вы ведь хотите это время для разработчиков сократить?
Код в PR в любом случае придётся прочитать и понять перед тем, как смёрждить. Так что про какую экономию вы ведёте речь совершенно не ясно.
Я понимаю, что код - это тоже язык, но его нужно сначала прочитать, потом понять - это время.
Было бы о чем говорить, если бы в PR за три недели, прошедших с момента публикации, зашел хоть один разработчик и оставил хоть какой-то комментарий. Пока что время, потраченное разработчиками, ровно ноль. Я потратил в бесконечность раз больше своего времени на это, сходите и подумайте об уместности ваших претензий.
Я потратил в бесконечность раз больше своего времени на это, сходите и подумайте об уместности ваших претензий.
Вы потратили его по собственному желанию, и предполагаете, что на разработчиков это налагает какие-то обязательства оценить эти времязатраты. Не налагает. Что мне делать по поводу моих "претензий" я разберусь самостоятельно.
Если приводить еще одну аналогию, Вы на статью на Хабре потратили "в бесконечность раз больше своего времени" по сравнению с описанием своего PR. Кроме того, в самом PR ссылки на эту статью или ее перевод нет.
Пока что время, потраченное разработчиками, ровно ноль.
Это утверждение базируется на отсутствии комментариев, сколько времени было потрачено по факту - нам неизвестно.
В качестве камня в огород разработчиков могу отметить, что они сами же пишут:
We'd love contributions!
И затем заодно как будто не замечают мои сообщения в пм в слаке. Хотя вроде бы этот человек не OOO.
и предполагаете, что на разработчиков это налагает какие-то обязательства
Не придумывайте, я такого нигде не говорил. Я отвечал вам на ваш комментарий с упреком (не от разработчиков, а от вас).
Давайте ещё раз попробую объяснить. Таймлайн здорового человека:
Я шлю PR
Разработчики смотрят, говорят, нужно ли им это вообще, спрашивают что им не понятно в тех местах, где им хочется разобраться (есть вероятность что им вообще пофиг что внутри происходит, пока тесты выполняются)
Я отвечаю на вопросы
Пока что мы дальше первого пункта не продвинулись.
У меня ноль претензий к разработчикам. Отпуска, дедлайны, мало ли что. Но вот ваши эмоциональные нападки «кинули в лицо таблицы бенчмарков» это просто неуместно.
Мой изначальный пост был о том, что вы создали очень подробную статью, на которую в итоге не сослались в PR, и сам PR, котором, кроме бенчей, описания подхода нет.
Я предполагаю, вы хотите начать диалог с раработчиками после того, как они посмотрят Ваш PR, и скажут - нужно, не нужно?
Я также предположил, что разработчики охотнее посмотрят Ваш PR, если в нем будет подробное описание "что/зачем/почему", исходя из опыта компании, в которой я работаю, и которая относится к Wolt. Это мое предположение тоже может быть ошибочно, но из того, что я вижу - это сильно увеличивает шансы, т.к. дается контекст и описание приложенных усилий.
Далее я упомянул Ваш PR и Вашу статью непосредственно Дагу. Возможно, если он не ответит, я ретранслирую это в их общий канал.
В чем состоит "эмоциональность" нападков, в словах "кинули в лицо"? Ок, без проблем. Не знал, что это заденет.
Далее я упомянул Ваш PR и Вашу статью непосредственно Дагу.
Ну как, действительно дело было в недостаточном сопровождении PR?
Вы у меня спрашиваете, что сейчас в голове у разработчиков, которые держат этот репо? Вряд ли ответ Вас удивит - я не знаю.
Ответа от Дага я пока не получил, но после Вашего сообщения ретранслировать в их общий канал желания поубавилось.
И я в третий раз повторю, исходя из моего опыта - при всех прочих равных и даже при максимальном раздолбайстве разработчиков - PR, снабженный подробным описанием, выиграет у пустого, если изменения в коде будут одинаковыми.
бенчмарки это самое важное.
Я вроде бы не говорил, что бенчмарки не важны, я говорил про пояснения к ним.
Код в PR в любом случае придётся прочитать и понять перед тем, как смёрждить. Так что про какую экономию вы ведёте речь совершенно не ясно.
Если для Вас снабдить PR описанием подхода и в целом "зачем это нужно" - это излишнее "уважение" - ок, не пишите. Я, честно говоря, не знал, что "я сделал" = "срочно в релиз!"
Я предложил то, что, считаю, ускорило бы процесс мерджа. Если это, конечно, и есть цель, а не статья на Хабре.
Я ускорил генерацию blurhash в 3̶6̶ 8̶7̶ 128 раз