Комментарии 38
Отличная статья, спасибо. Пожалуйста, пишите ещё по этой теме, а если будут разборы частных случаев -- вообще супер.
я так понимаю эти все расширения с родни нвидиовским tensor core ?
есть ли информация сколько АМХ у интелов ? у каждого ядра или нет ?
как они работают с hyperthreading ?
что на счем AMD CPU ?
идеологически похоже, хотя есть нюансы.
Про такие детали Intel AMX внутри Sapphire Rapids мне информация не попадалась, но, возможно, кто-то из коллег в курсе.
У AMD, насколько можно судить по спецификациям, AMX пока нет. В этом исследовании можно глянуть последние бенчмарки OpenVINO -- Sapphire Rapids с меньшим числом ядер за счет AMX показал результат выше EPYC 9684X с VNNI (AVX-512 Vector Neural Network Instructions).
Все эти векторные и матричные расширения существенно увеличивают размер регистрового файла. Мне интересно как это сказывается на времени переключения контекста в Unix подобных операционных системах ? На сколько деградирует производительность ОС на процессорах с такими раширениями ? Ведь системе, при переключении задач, приходится сохранять гигантские обьемы данных во внешней памяти, перекачивать (прогревать) заново все кэши. Как это все работает, и не является ли более оптимальным держать векторно-матричный вычислитель в виде отдельного блока (читай GPGPU) ?
Разработка таких расширений включает в себя не только аппаратную часть, но и большую работу со стеком ПО: помимо оптимизаций математических библиотек, это в том числе и организация поддержки расширения в ядре Linux (детекция наличия/отсутствия расширения, указание режима его работы, флаги используемых регистров для оптимизации переключения контекста и т.д.). Например, краткое описание в документации для Arm SME (понятно, что это не все "внутренности", а только минимальная видимая часть).
Активная работа расширения происходит только во время решения вычислительных задач, для которых эти матричные операции -- серьезные хотспоты (до 90% времени работы приложений, о которых говорилось в тексте, может уходить на матричное умножение, например). Получаемое в итоге ускорение компенсирует возросшие накладные расходы -- на практике по сравнению с векторизацией минимум в 2 раза ускорение получается.
Что оптимальнее -- расширение cpu или отдельный блок -- здесь нет однозначного ответа: есть примеры удачных решений как первой категории, так и второй. Тот же упомянутый в статье гомогенный Fujitsu Fugaku.
Что оптимальнее -- расширение cpu или отдельный блок -- здесь нет
однозначного ответа: есть примеры удачных решений как первой категории,
так и второй. Тот же упомянутый в статье гомогенный Fujitsu Fugaku.
Валерия, мы говорим о процессоре общего назначения или о специализированном ?
Если о GP CPU, то я хочу увидеть цифры и результаты симуляции на реальных смешанных задачах. Какой оверхед создает добавление матричного расширения в ядро, как часто испольуется это расширение пользовательскими приложениями и какой суммарный прирост производительности мы получим. Логика подсказывает, что из тысячи пользовательских приложений (процессов) только одно будет использовать ускоритель с пользой (да и то не постоянно), для остальных 999-ти процессов наличие этого ускорителя является существенным замедлителем из-за оверхеда связанного с переключением контекста.
Если вы сможете провести такие замеры, будет очень интересно посмотреть. Например, Apple M4 c Arm SME или Apple M1--3 c Apple AMX.
К сожалению, моих ресурсов для такого исследования недостаточно. Я как раз надеялся на Вас и вашу компанию в этом вопросе. :-) Проблема еще в том, что яблочные M1-M4 полностью закрыты, документации нет. Но если вы взялись проектировать ускоритель для процессора общего назначения, то хотя бы какую-то прикидочную симуляцию/моделирование выполнить стоит.
Возможно в тексте немного затерялось -- я инженер-математик по образованию, призванию и роду деятельности), т.е. применительно к этим расширениям я -- пользователь, а не разработчик. Ускорители я никогда не проектировала и не проектирую, но, как математика-прикладника, меня, конечно же, интересует, что есть в этой области, чего ждать.
Думаю, разработчики железа проводили исследования, о которых вы упоминали, ведь те же Apple M1--4 не только для инференса и коррекции фото используются, но статьи о серьезных просадках производительности на других приложениях мне не попадались, пока я исследовала данную тему.
Проблема еще в том, что яблочные M1-M4 полностью закрыты, документации нет.
Вы о чём? Кем закрыты?
Документация на сайте ARM.
Если нужны детали реализации процессора, тут море информации:
Видимо Вы потеряли нить повествования. Речь идет о матричном сопроцессоре и GPGPU. Эта тема для M1-M4 полностью закрыта, есть только пользовательские библиотеки с весьма ограниченным функционалом. Оценить производительность операционной системы по ним вряд ли выйдет.
Видимо Вы потеряли нить повествования. Речь идет о матричном сопроцессоре
Я не терял. Не могу залезть к вам в мысли, чтобы понять, что вы хотели сказать.
Потому что сказали вы другое:
яблочные M1-M4 полностью закрыты, документации нет.
И это является ложью.
В M4 сопроцессор AMX выведен в виде SME.
Документация открыта на сайте ARM.
Вы ведь проигнорировали ссылку в статье?
https://github.com/tzakharko/m4-sme-exploration
В M1-M3 он является недокументированным, и разработчикам предлагается использовать фреймворки. Но это не мешает его программировать напрямую, если захочется (для себя).
Документация на AMX легко доступна в 1 клик.
https://gist.github.com/dougallj/7cba721da1a94da725ee37c1e9cd1f21
Вот так вот информация "закрыта" чуть более чем полностью(с)
Пассаж про GPGPU тем более не ясен. Всё открыто в презентациях и документации Apple.
Не буду с Вами спорить, а всего лишь попрошу продемонстрировать сниппет (кусочек) ассемблерного кода для AMX в составе процессора M4 для вычисления перемножения двух матриц 16x16 с элементами FP16. Это не должно быть для Вас большой проблемой - вся спецификация, с Ваших слов, есть в презентациях от Apple.
В M4 не Apple AMX, а Arm SME https://developer.arm.com/documentation/ddi0602/latest/SME-Instructions
Валерия, мне комментарием выше пишут, что
Документация на AMX легко доступна в 1 клик.
Откровенно говоря мне глубоко фиолетово на сорта говна в яблочной продукции.
а, Apple AMX из Apple M1--M3 -- возможно, имелось в виду исследование Дугалла Джонсона, который его как раз и обнаружил, https://gist.github.com/dougallj/7cba721da1a94da725ee37c1e9cd1f21
смею предположить что матричные регистры вообще не сохраняються при переключении, а содаеться очередь команд от разных контекстов. и если существует несколько пытающихся использовать AMX они ждут друг друга, один поток выполнил часть , результат сохранился в L1/L2 и другой получит время , и все это на железном уровне. хм (мысли в слух) получаеться как с hyperthreading. вот только особого практического смыла в этом мне не видиться
смею предположить что матричные регистры вообще не сохраняються при
переключении, а содаеться очередь команд от разных контекстов. и если
существует несколько пытающихся использовать AMX они ждут друг друга,
один поток выполнил часть
Такое возможно только если взаимодействие с матричным усокрителем происходит через ядро ОС, через syscall. Иначе как заблокировать (ожидать) окончания исполнения потока команд выполняющих рассчет матриц ? Ведь пользовательскому процессу запрещено блокировать прерывания, тем более от таймера переключения задач. А если мы работаем через ядро, то нафига нам вагон этих инструкций в вычислительном ядре, не будет ли более логичным унести их наружу в отдельное устройство со своими кэшами и т.д.
Я задал этот вопрос в надежде, что автор статьи провел какие-то исследования в этом направлении и имеет расчетные данные по оверхеду. Тут я полностью придерживаюсь количественного подхода Д. Паттерсона - мы можем впихнуть впроцессор сколь угодно навороченный функционал, но перед этим мы обязаны произвести кое-какие математические выкладки и понять как этот новый функционал повлияет на исполнения всех остальных, не специализированных задач.
В общем, я категорически против того, чтобы из RISC-V делали очередной CISC. Если требуется матричный вычислитель - ставьте отдельный IP блок GPGPU, посадите его на внутренний интерконнект, снабдите вагоном статической памяти не опираясь на размер регистрового файла. Но раздувать ядро и ISA это просто глупо!
не совсем
выполнение любой ассемблерной команды может зависить от предыдущих и pipeline может так сказать замереть.
но в случае с Intel AMX судя по всему произходит как вы предположили : через syscal
https://www.intel.com/content/www/us/en/developer/articles/code-sample/advanced-matrix-extensions-intrinsics-functions.html
"2. The next section in the code is to to invoke a Linux system call to request access to Intel® AMX features. This is performed using the arch_prctl(2) based mechanism for applications to request usage of the Intel® AMX features. Specific information is described in the Linux kernel documentation. "
добавление акселераторов делает ли из RISC CISC ? xм .... не уверен :-)
MMX, SSE , NEON , SVE - все это акселераторы :-)
да таже nvidia с тенсорами.
а вот перегонка данных между процессорами очень затратное действие
особенно в сегодняшней PC aрхитектуре.
не зря Apple сделала свой чип
добавление акселераторов делает ли из RISC CISC ? xм .... не уверен :-)
MMX, SSE , NEON , SVE - все это акселераторы :-)
да таже nvidia с тенсорами. а вот перегонка данных между процессорами очень затратное действие особенно в сегодняшней PC aрхитектуре.
Вот это меня и беспокоит - постояно, без дела, сохранять и восстанавливать горы регистров MMX, SSE (NEON, SVE) это лишняя работа для процессора и пагубно сказывается на кэшах. Если к этому добавится регистровый файл матричного ускорителя, то производительность системы упадет еще сильнее на обычных задачах.
Если я не ошибаюсь, у яблочного M1 ускорители вынесены и стоят рядом на отдельных кристаллах чиплета, только вместо PCIe - своя внутренняя высокоскоростная шина. Секретные инструкции недоступны пользовательким процессам, работа с ними только через систему. То есть это ничем не отличается от внешнего устройства типа GPGPU, но более эффективно за счет более тесной и более скоростной связи.
Apple M1 chiplete

есть разница между инструкцией с расширеным функционалом и акселератором расположеном на одной подложке.
может быть разница не очень заметна но она есть.
да раньше работа с акселерарором выгляделала как запись особого значения в специальный адресс , как будто запись в память
сегодня добавления intrinsics в комиляторы стало обыденным делом
и то что выглядит как ассемблерная инструкция не обязательно являеться таковой.
как реализовано у Apple я не знаю, они предоставляют высокоурвневые API из которых точно не возможно понять детали без дизассемблирования кода.
так же Neural Engine наверное выполняет больше функций чем просто AMX
я предпопагаю что там так же армовские ядра типа Cortex Mx с различными акселераторами
и да именно тесная интеграция и использование одних кешей позволяет им добиться лучшей производительности
но это точно также верно и для AMX.
вот другой примет NEON у ARM
только у RISC части есть instruction fetch и decode
кооторый используеться для всех инструкций в том числе NEONoвских
но разных частей процессора dаже разные длины конвееров
у RISC - 13
у NEON - 10
и на самом деле NEON это DSP aксселератор тестно интегрированый в процессор
https://en.wikipedia.org/wiki/ARM_Cortex-A8
на 3тей странице картинка
https://web.archive.org/web/20150101062932/http://www.arm.com/files/pdf/A8_Paper.pdf
ну и ... основная разница между RISC и CISC это
размер машинной инструкции
у риска она посноянная у сиска переменная
все остальны различия и особенности вытикают только из этой особенности
в этом плане Интелы попрежнему CISC
а все Армы RISC.
но интелы отстают по производительности не поэтому
они тащят за собой огромный хвост легаси поддержек.
где была информация что до 40% площади чипа нужны только для этой подержки.
как только кто то у них решиться обрубить этот хвост - они "побегут" :-)
но мне не вериться что они способны на это ...
есть разница между инструкцией с расширеным функционалом и акселератором расположеном на одной подложке.
может быть разница не очень заметна но она есть.
Разумеется. Об этом чуть ниже.
да раньше работа с акселерарором выгляделала как запись особого значения в специальный адресс , как будто запись в память
сегодня добавления intrinsics в комиляторы стало обыденным делом
и то что выглядит как ассемблерная инструкция не обязательно являеться таковой.
Наличие отдельной инструкции в вычислительном ядре далеко не означает, что аппаратная часть её реализующая должа быть тесно интегрирована в это ядро. Вспомните математческий сопроцессор i387 - это отдельный рядом стоящий камень, тем не менее инструкции для работы с ним были внедрены в центральный процессор i386. Однако такое расположение дел создавало массу неприятностей для многозадачных операционных систем, так как приложение должно было вызывать инструкцию fwait для ожидания завершения работы мат сопроцессора, а это ставило на паузу весь праздник.
На счет разницы между отдельной инструкцией и портом I/O. Если задача решаемая на акселераторе требует (и поддается) нарезанию на очень мелкие кусочки, то лучше иметь отдельную инструкцию, так как это экономит на оверхеде связанным с syscall-ом. Но в этом случае получаем дополнительный оверхед с переключением контекста (сохранением состояния акселератора при смене таска). Если же задача более крупная (инфёренс нейросетей), то лучше иметь отдельное устройство и общаться с ним через I/O. В этом случае у нас нет оверхеда связанного с переключением контекста, но есть оверхед связанный с syscall-ом и загрузкой данных в акселератор. Если обеспечить акселератор прямым доступом к памяти, то часть этих проблем снимается.
Где баланс и как лучше - вопрос открытый. Собсно мой первый вопрос к автору статьи как бы намекал на это - рассказать нам про пропроблемы тех и других вариантов, что является наименьшим злом и в каких случаях. :-)
ну и ... основная разница между RISC и CISC это размер машинной инструкции у риска она посноянная у сиска переменная все остальны различия и особенности вытикают только из этой особенности
Боюсь Вас наглым образом обманули. :-) Размер машинного слова имеет третьестепенное значение для идеологии RISC процессора. Да, так оказалось, что при фиксированной ширине машинного слова декодер получается проще и быстрее. Основное же в RISC это обьем выполняемой работы за одну инструкцию - чем её меньше, тем более рисковый процессор. Отсюда вытекает второй идеологический постулат - система команд должна быть ортогональной, то есть не содержать команд выполняющих дублирующие функции, даже частично. Третий идеологический постулат RISC - ширина одной ступени конвейера должена быть как можно уже, то есть занимать минимально возможное время на свою работу (иными словами, критическая длина цепочки логических элементов в составе одной ступени долна быть минимальной). В теории это должно позволять поднимать тактовую частоту до немыслимых высот. Появление RISC процессоров в десятки раз подняло тактовую частоту к середине 90-х и то была настоящая революция! Однако, уже в конце 90-х в RISC процессоры начали наталкивать всякого рода SIMD и векторные инструкции (UltraSPARC, MIPS, позже ARM со своим Neon). Это существенно увеличило обьем выполняемой работы за такт, как следствие - ступени конвейера стали тяжелыми, их стало много, рост тактовой частоты затормозился несмотря на постоянный прогресс в литографии и увеличении плотности кристалла. Появления ускоряющих инструкций это полное отречение от идеологии RISC! Именно по этому ARM уже давно не является RISC процессором, не смотря на название. :)
Меня давно мучает вопрос - какова могла бы быть максимальная тактовая частота ядра RV32IMFC выполненного по современным технологическим нормам (скажем, 3нм). Что-то мне подсказывает, что планка в 10ГГц была бы легко преодолена. И тут стоит вопрос - а может быть послать в гнездо все эти акселераторы ? Может процессор с очень простыми ядрами работающими на 10ГГц и со сверхмассовым их количеством это гораздо более интересное и производительно решение ? Помоему Nvidia двигается как раз этим путем и как мы видим - весьма преуспела.
но интелы отстают по производительности не поэтому
они тащят за собой огромный хвост легаси поддержек.
где была информация что до 40% площади чипа нужны только для этой подержки. как только кто то у них решиться обрубить этот хвост - они "побегут" :-) но мне не вериться что они способны на это ...
Полностью согласен, даже более того. У меня есть мнение, что у Интела нет шансов кроме одного - быстро пересесть в уходящий поезд RISC-V. То есть использовать свою высокопроизводительную супескалярную ОоО микроархитектуру, но заменить систему команд на RISC-V - тем самым послав подальше всё легаси и освободив место на кристалле под еще большее число ядер и кэша. Или для матричного вычислителя. :-)
на счет RISCa все вы правильно описали
но все это следствие фиксированой длины команды
которая ограничела набор команд
и заставила выбрать только простые и элементарные
которые в свою очередь требуют меньше транзисторов
фиксированый размер дал возможность уменьшитьшину адрессов для команд ... не намного, а 1,2 (сегодня на 3) разряда
но это относительно длиные пути в чипе
а максимальная частота чипа ограничиваеться самым длинным соеденением задействованым в процессе.
по той же причине
удлиняют конвеер, разбивают на простые этапы что бы уменьшить схему и сократить пути.
первые армы были с 3мя этапами, потом долгое время с 5ю
теперь 13 ...
кстти у этого подхода есть недостатки :-) вспомним чем 21 (кажеться)этапный обернулся для П4 ...
и более современные проблемы с предзагрузкой команд.
Интел давно уже внутри работает как RISC и запускает микрокод.
а его попытка с Itanium ... долго низенько летела , но взлетела ...
я так же имел дело с Xeon Phy , первой версии на отдельной карте
отличный был процессор
было много энтузиазма вокруг нео
с некоторыми упрощениями
можно сказать это был массив Atom ядер каждое из которых поддерживало Hyperthreading
если я не ошибаюсь у меня была версия с 64 или 128 ядрами
каждый был самостоятелным, на каждом бежал х86 код ...
но ... они не завезли туда (в целях экономии) ни ММХ ни SSE
и все , мир не принял :-)
(как и фирма где я работал)
недают Интелу слезть с их наборов инструкций
его берут только для совместимости
как с существуюшим софтом так и знаниями програмистов :-)
вот смениться поколениЯ :-) и все интелу крышка
Во многих решениях векторный или матричный ускоритель работает сразу с L2, без нагрузки на L1. Это позволяет использовать широкую шину данных для загрузки-сохранения. В RISC-V расширения с собственным набором регистров имеют отдельный флаг использования. Якобы это может позволить сократить время переключения контекста. На мой взгляд, отдельный вычислитель нужен если реально нужна высокая скорость. А если просто побаловаться, то можно и в процессор расширение :)
Даже для высокой скорости не всегда нужен отдельный вычислитель: в свежем июньском Top500 далеко не все машины гетерогенные, тот же Fujitsu Fugaku (ядра A64FX) до сих пор находится в топ-5. В инфографике можно посмотреть сравнение c ускорителями, правда 2021 г., но интересно.
А если просто побаловаться, то можно и в процессор расширение :)
Проблема в том, что уже само наличие такого расширения вносит существенные поблемы с переключением контекста, так как ОС обязана сохранять состояние для каждого процесса, независимо от того используется это расширение пользовательским процессом или нет. Иными словами, добавив поддежку матричных инструкций в ядро ОС мы тут же радикально ухудшаем производительность всей системы целиком.
Как вариант, сделать так, чтобы инструкции матрично-векторных расширений не были доступны пользовательскому уровню (в терминах RISC-V - только для Supervisor mode), а взаимодействие происходило бы через ядро операционной системы, т.е. через отдельный syscall. В этом случае сохранять каждый раз состояние огромного векторно-матричного регистрового файла при переключении контекста не потребуется, его нужно будет сохранять и восстанавливать только при выполнении пользователем syscall-а (либо сделать доступ атомарным). Фактически, работа с векторно-матричным ускорителем превращается в работу с отдельным "виртуальным" устройством через стандартный API операционной системы. Если немного подумать, то становится понятно, что такое устройство надо вынести в отдельный кусок железа за переделы вычислительного ядра и таким образом мы получаем GPGPU.
То есть, ядро при переключении таска, отключает расширение, и если другой таск пытается его использовать, бросается исключение и обработчик сохраняет контекст
Интересное решение, спасибо.
И там делее по тексту:
AMX is accessible only to applications that invoke a new system call to request access. When a user invokes this system call, they agree that if they use an alternate signal stack, that they are providing an alternative signal stack of sufficient size. The simplest way to do that is to use the updated ABI in glibc 2.34 or later [8][9], though they could Also use their own calculation or ask the kernel directly [3].
То есть без отдельного syscall-а не обошлось, приложение обязано спрашивать систему разрешенея всякий раз когда оно хочет поработать с AMX. Система при этом резервирует от 8К до 64К на процесс для сохранения контекста AMX .
Если смотреть на Intel AMX, то по умолчанию матричные регистры отключены. Их включают перед использованием и выключают после них. На сколько я понимаю, как раз чтобы избежать указанные вами проблемы с переключением контекста.
Где-то я это уже видел, я конечно далёк от всей этой красоты, но статья прекрасная. Большое спасибо за труды, за статью, продолжайте. И конечно же успешного развития этой тематики и физической реализации!
Простите за возможно глупый вопрос, но если расширение умножает матрицы 16х16, а мне нужно перемножить 512х512, то как это сделать? Нужны же целые строки и столбцы.
Отличный вопрос, если какие-то математические детали неясны -- не стесняйтесь спрашивать!
В начале статьи кратко были описаны идеи многоуровневого блокирования из статьи Goto&Geijn: большие матрицы в несколько этапов разбиваются на маленькие блоки. И вы аккумулируете результаты умножения этих блоков. Ведь что такое элемент матрицы-произведения? Это сумма произведений элементов строки первого сомножителя на соответствующие элементы столбца второго сомножители. А раз это сумма, то вы можете просуммировать не все сразу, а частями -- идя по подстрокам и подстолбцам.
Есть чуть более хитрый подход, когда вместо скалярного произведения используется внешнее -- тогда аккумулируются не отдельные элементы, а целые блоки. Подробнее о нем я расскажу в следующем тексте. А в последнем тексте будет детально рассмотрен пример реализации такого алгоритма.
Если интересует практика использования AMX и как его применять для реального умножения матриц, то могу порекомендовать: https://habr.com/ru/articles/807033/
Панорама матричных расширений: от x86 до RISC-V