All streams
Search
Write a publication
Pull to refresh
1
0

3D-Graphics Developer | Team Lead

Send message

Которые, в свою очередь, состоят из более элементарных зашкварков

Логично, что flat версия тогда профитна, когда размер элемента со всеми вспомогательными полями кратно меньше размера кэш линии. Это заметно повышает шанс, что несколько элементов буду отпроцессены из одной кеш линии. Если размер элемента больше кеш линии, то уже в целом плевать где там в памяти раскиданы элементы, так все равно попадаем на чтение новой кеш линии, и вероятный поход в оперативку. Следующий порог по перфу - это когда элемент уже больше страницы памяти, т.е больше 4кб. Тогда мы ещё к промаху по кешу прибавляем вероятный промах по кешу TLB, тк он вовсе не резиновый. Но удаление flat контейнера будет однозначно быстрее при любом размере.

На примере разработки плюсовой графики (c++/hlsl/glsl/dx/vulkan и тд) есть вечный недостаток квалифицированных спецов способных тащить проект, а не только эффективно выполняющих указания "сверху". Зато джунов в избытке. Но у 95% квалификация уровня "штанишки не спадают, пропеллер на кепочке крутится - и слава богу". Имею богатый опыт проведения собеседований включая беседы с джунами и пробы дать им шанс. Для себя сделал вывод: шанс имеет только тот джун, что покажет живой интерес к предметной области, в которой ХОЧЕТ (ведь хочет, да?) найти работу - уверенная теоретическая база, и желание разбираться в сопутстующих технологиях, техниках, и тп. Это желание не абстрактное в виде самопрезентации в резюме, а выражено в конкретных пет проектах, усновно, на гите, где в качестве темы выбрана какая-нибудь новомодная техника/нейронка/вайтпейпер/идея, где джун ее героически решил, красиво все оформив и расписав в readme. Все те, кто вместо демострации пет проектов лишь клялись в резюме какие они хорошие у мамы программисты, на практике быстро тонули либо на собесе, либо на испытательном, и приходилось с ним расставаться - столько сеньеров нет в природе, чтобы тащить такое количество джунов ясельного уровня, как и нет такого количества задач, которые можно поручить такому количеству джунов, на которых они могли бы тренироваться годами. Поэтому шансы найти работу повышаются, если для начала определиться с областью, в которой есть спонтанный интерес - туда и копать. А какий язык, какие инструменты - имхо дело десятое. С++ действительно лютоват для начала, но все реально, было бы желание.

Хочу поделиться практическим опытом в качестве разработчика, переводившего одну широко известную в узких кругах игру на фэншуйные многопоточные рельсы. Фэншуй заключается в следующем: на старте игры анализируется топология процессора; далее, с учетом знаний о вендоре, знании о P/E-ядрах, структуре кешей L3, количестве ядер, наличия SMT/HT, и тд, физические ядра распределяются на группы в соответствии с их будущей специализацией (графика, логика, IO, звук, инпут, и т.п.). На основании полученных знаний, и в том числе, например, с учетом рекомендаций с сайта самого Intel’а о том как для игры реального времени оптимально распределять потоки на их гибридных процессорах, определяются максимальные размеры пулов потоков, их affinity-маски. На уровне Windows выставляются соответствующие приоритеты памяти, приоритеты исполнения, возможность троттлинга. Группировка также происходит по использованию ядрами общего кэша L3, если их несколько, чтобы уменьшить core-to-core latency и снизить вероятность cache-thrashing, так как связанные потоки с большой вероятностью работают с общей памяти, тем самым выше шанс что кэш будет дольше оставаться “теплым”. В результате имеем достаточно неплохую масштабируемость (в разумных пределах гранулярности наших задач), работающую лучше, чем без учета топологии процессора.
По итогу внедрения и обратной связи от юзеров на целом зоопарке процессоров, включая экстремальные вещи типа AMD ThreadRipper PRO 5975WX (32 ядра, 64 потока, 4 кэша L3), вывод такой: основная головная боль происходит именно от процессоров Intel с гибридной архитектурой. В чего вдруг?

Связано это с конфликтом управления ресурсами процессора. Если совсем кратко - в идеальном мире каждый разработчик должен сам учитывать конкретную модель процессора и определять какие потоки должны выполняться на каких ядрах, их количество, и тд, (и никакие другие процессы не должны конкурировать одновременно с ним за тот же ресурс процессора). Но в подавляющем большинстве случае этого никто не делает, как и нельзя изолироваться от конкурентов за ЦПУ, и все это остается на откуп операционной системе - слишком долго мы жили в однопоточной парадигме, плюс есть множество крупного рогатого софта ныне живущего по 30 лет, который не так просто переписать на новую парадигму. В итоге с одной стороны имеем многочисленный софт, который живет строго в один поток, либо создает неадекватное количество потоков и не управляет ими (например, std::thread::hardware_concurrency() возвращает максимальное количество потоков с учетом SMT/HT и без учета энергоэффективности ядер, некий абстрактный гомогенный пул), и с другой стороны имеем ОС, которая одновременного жонглирует потокам всех процессов по всем ядрам, ничего не зная о структуре вычислительной нагрузки конкретного процесса (какие потоки важнее и должны иметь высокий приоритет, какие можно затроттлить, и тп) старается максимизировать общую пропускную способность процессора и всей системы, а не производительность конкретного процесса находящегося в foreground (в ущерб остальным процессам в фоне). Foreground-процесс оптимизируется косвенно за счет повышения его приоритета, и возможного буста некоторых ядер на котором вращаются его потоки. Но ОС не обладает даром ясновидения, и может полагаться только:

  1. на некоторые эвристики в оценке важности потока и паттерна его нагрузки на процессор

  2. некоторые хинты, которые может сообщить разработчик для своих потоков

Даже с учетом хинтов, все это провалится в планировщик потоков ОС, который для рядового разработчика является черным ящиком без возможности прямого управления. И в общем случае попытка освоить всю мощность процессора сталкивается с неэффективностью планировщика и другими проблемами, особенно когда речь идет о real-time игре, где множество микро-тасков, и где высокой FPS и низкий latency при передаче данных между потоками на разных ядрах - не пустой звук, особенно когда Windows - не операционная система реального времени.

Конфликт управления, как было сказано выше, на примере гибридных процессоров Intel, заключается в следующем:

  1. У каждого ядра есть некий режим Idle - когда ядро как бы спит, сокращая потребление электроэнергии до минимума ценой высокого latency при пробуждении, то есть переключение в режим idle и обратно штука не быстрая от слова совсем, когда мы ожидаем низкую core-to-core latency, а получаем тыкву. Основной смысл режима Idle - экономия электроэнергии и жизни батареи ноутбука, что уже как бы намекает, что эта история не про мощь и производительность, ведь так?...

  2. На гибридных процессорах также появился некий хардварный ThreadDirector, который на основании внутренних метрик и, ключевое слово, эвристик, выдает рекомендации ОС о том, как управлять ядрами - когда и что переводить в режим Idle, троттлить, и тп.

  3. ОС в праве не прислушиваться к рекомендациям ThreadDirector и может руководствоваться собственной политикой планировщика потоков, но, на сколько я могу судить, она таки это делает.

  4. При этом в Windows во главу угла поставлена PPM (Processor Power Management) - политика управления питанием процессора. Соответственно на основе плана электропитания ОС может агрессивно управлять мощностью ядер, вплоть до перевода их в режим Idle.

  5. Это приводит к распространенному явлению на процессорах Intel известному в Windows как Core Parking (на AMD подобного поведения пока не встречал), когда на основе всех эвристик и с учетом PPM операционная система принимает решение усыпить ядро, и вот с этого момента начинается ад. В реальности эти эвристики косячат. Разработчик мог рассчитывать именно на это ядро, т.к. оно входит в группу на котором вращался пул потоков быстрого реагирования - задачи выполняющиеся с какой-то периодичностью (условно 40% времени потоки привязанные к этим ядрам курят бамбук на семафоре, частота таких переключений вкл/выкл пропорциональна FPS в игре, и важна скорость включения этих ядер в работу). В итоге по эвристике ОС может решить что потоки не достаточно важны, и ядро можно усыпить несмотря на все affinity-маски и приоритеты потоков. В итоге пробуждая поток по семафору мы внезапно попадаем на latency при переходе ядра из режим Idle в боевой режим. Что и наблюдается у части счастливых обладателей гибридных процессоров, и выражается это во всевозможных фризах… Лечится принудительным отключением Core Parking (да-да, руками юзера в реестре или через PowerShell), но это не то на что я как разработчик могу повлиять, нет такого API через который я мог бы сообщить ОС убрать свои грязные лапы от моих блестящих ядрышек и оставить их в покое.. Скажу больше, даже если поставить план электропитания на максимальную производительность, Core Parking отключается только у E-ядер, а у P-ядер - никогда! Классно же, да?

Я могу ошибаться, но на мой текущий взгляд вся эта гибридность идет точно не от желания поразить пользователя выдающейся мощность процессора в самое сердечко, а про что-то другое… Для меня выглядит парадоксальным и абсурдным вводить эти добровольно-принудительные политики ограничения питания процессора особенно на десктопных машинах, в то время как от разработчика ожидают противоположного, что он снимет максимальную мощность с их любимого процессора. Начинаешь снимать мощность - получи троттлинг и дикие latency. Да, я понимаю что нагрев процессора - не пустой звук, но проблема этой гетерогенности ядер еще в том что в нагрузку вместо одного L3 кэша идут несколько кэшей с повышенной core-to-core latency между ядрами с разными L3. Синхронизация кэшей L1/L2 через синхронизацию L3 между P/E ядрами стоит дороже чем между P/P или E/E. Это уже накладывает отпечаток на то какой многопоточность, в широком смысле, должна быть - это максимально изолированные задачи, которые живут на своих типах ядер и практически не пересекающиеся по используемой памяти, чтобы избегать как огня синхронизации кэшей L3 (о фэнтезийный, дивный мир). 

При текущем раскладе вся эта история с гибридными процессорами плохо ложится на паттерн использования, когда от всей широты ядер ожидается высокая пиковая пропускная способность и низкая latency. По факту в случае с Intel реально работают только P-ядра, а E-ядра можно использовать для всяких балластных задач, которые, по сути, ни на что не виляют, и в таком количестве для игры просто не нужны... Какой-нибудь офлайновый рендерящий софт таких проблем не имеет, т.к. для него стихийные задержки в несколько миллисекунд не существенны, в то время как целый кадр игры надо распараллелить, посчитать, и собрать, например, при 120 FPS быстрее чем за ~8 мс, и здесь задержка в пару миллисекунд уже критична.

Стоит отметить, что на некоторых процессорах AMD также присутствует гетерогенность, но, по крайней мере, у них нет режима Idle и принудительного Core Parking я еще не встречал.

Information

Rating
Does not participate
Registered
Activity