Pull to refresh

Comments 67

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

Вот с этим вот, конечно, невозможно не согласиться. Только надо понимать, что всё остальное содержание статьи имеет такое же отношение к конструированию процессоров, как вот этот вот рисунок имеет отношение к космическим полётам.

То есть да, некоторое количество здравых идей имеется, но они густо перемешаны с вещами, которые ну настолько просто дикие, что дальше ехать просто некуда. Ну вот как какой-нибудь “химический отсек” в этой самой Знайкиной ракете: к чему он там? Вот и предложение поделить адресное пространство пополам на ОЗУ и порты ввода-вывода — это из той же оперы.

P.S. А если это не что-то, что вы собираетесь реально реализовывать, а заготовка для главы в Научно-Фантастической книжке — то об этом нужно было сразу сказать, в таком разрезе это — ноборот, весьма глубокая и хорошо продуманная работа.

Ну вот как какой-нибудь “химический отсек” в этой самой Знайкиной ракете: к чему он там?

думаю, что это удобрения. ведь Знайка везет семена. все логично :)

А если это не что-то, что вы собираетесь реально реализовывать, а заготовка для главы в Научно-Фантастической книжке — то об этом нужно было сразу сказать, в таком разрезе это — ноборот, весьма глубокая и хорошо продуманная работа.

Так я в самом начале статьи написал, что представляю свои мысли по альтернативной архитектуре процессора.

Смешно даже думать что сейчас на основании этих идей побегу куда-нибудь с просьбой сделать процессор !

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

К сожалению я уже не программирую лет 20. Навыки растерял. Если кто возьмется помочь, это было бы здорово !

Про Мультиклет - читал я их документацию, но они упирают что у них не-фон-Неймановская идеология (а по сути - программа тупо ждет пока ей данные передадут). Хотя название "параграф" я у них подсмотрел.

Статья была интересна в контексте ситуации, но уровень идеи, как заметили выше, больше подходит развлекательным ресурсам.

Здоровый интерес к теме архитектуры можно только поддержать и вы молодец. Посмотрите как можно реально пощупать архитектуру RISC-V(Syntacore на ChipEXPO даже очно учить будут). Можете освоить ее, после попробовать симулировать свой рабочий процессор внутри ПЛИС-ины и сравнить!

И уже к этому сравнению статья была бы вообще самый сок

Удачи!

Спасибо!

Эх, было бы свободное время... На выставку не успеваю попасть.

Похоже на некий "транспонированный" VLIW, где инструкции длинные не в ширину, а в длину. Полагаю, архитектура наследует недостатки VLIW, касающиеся плохой на практике компиляторной оптимизации.

Браво! Вы уловили самую главную идею - я сначала думал именно о замене команд внутри vliw на небольшие последовательности микроопераций.

Позже пришла идея "варианта Б" - когда одна линейка команд выполняется параллельно на разных процеонах (этакий SIMD в ЦП).

Собственно, первый комментарий достаточно точно оценил уровень данного предложения.

От себя лишь замечу, что если выбросить все технические детали, а сконцентрироваться на идее попытки распараллеливания микро-потоков, то я знаю о нескольких попытках реализовать такого рода идею в железе (в достаточно серьёзных компаниях). Все они на текущий момент ничем не закончились ( в том смысле, что до реальных пользователей не дошли).

Но за попытку плюс.

Есть легенда, что в Гугле запрещено улучшать что-то на 20%. Улучшение должно быть значительное, например на 80% и более.
Нет смысла пытаться изобрести велосипед. В этом нет заинтересантов. Заранее понятно, что улучшение если и будет то незначительное.
Нужно значительное улучшение в решении каких-то задач. Например, вычислитель на мемристорах.
Квантовый компьютер. Или какая-то биологическая форма в виде кактуса в горшке.
Если привычный кремний, то покажите где будет гешефт.

Есть также легенда, что в том же гугле (вот совпадение-то!) борются за доли процентов быстродействия поиска, т. к. на их объемах это огромные величины суммарно.

Но что-то мне подсказывает, что истина где-то посередине.

Про Гугл не знаю, но в авиастроении есть правило «менять не более 10% за раз»: новая конструкция должна отличаться от предыдущей не более чем на 10%.

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

Уже есть множество гибридных чипов, сочетающих обычное процессорное ядро и FPGA, но они не умеют быстро перепрограммировать свою FPGA-часть на лету. Она прошивается заранее под конкретную задачу. Для процессоров общего назначения это не вариант, т.к. задача меняется с каждым квантом времени ОС. Нужен чип, умеющий перепрограммироваться за то время, пока переключается контекст.

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

у вас получился интересный взгляд на мой утренний порыв создать новую архитектуру :) Ваша подсказка тоже подходит под описанный мной вариант. Но моя мысль базировалась на одной работе , которую я пытался сделать лет 12 назад. Я активно занимался Н264. Сначала пытался допилить скачанный иоткуда-то аппаратный вариант, но очень быстро устал. Решил сделать вариант на многих ядрах ARM v6.Получилась у меня система вроде из 5-ти армов, сеть каждый с каждым, и каждый решал свою задачу в общем потоке. Но очень быстро обнаружилось, что простую обработку ядра делают хорошо, а вот всякую интерполяцию, интраполяцию, DCT, фильтрацию, YUV-RGB очень медленно. И я добавил в систему узел который все эти задачи решал очень быстро по запросам от ядер. Вся система была в FPGA. И это более-менее работало. Потом я взял арм с НЕОН , открытую библиотеку OpenMAX и этот вариант обогнал мой многоядерный :) Но вот сам многоядерный подход с перепрограммируемым под задачи супервычислителем мне понравился.

У меня описан немного другой подход - процеон это минимальный вычислительный блок для самых простых операций, но которые могут объединяться в ядре для выполнения сложной команды-параграфа.

А насчет суперFPGA я не совсем понял. Можно пояснить ?

точнее было бы написать "придумать", а не пояснить :)

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

Intel тоже пыталась изобрести что-то новенькое, отличное от RISC и x86 - получился Itanium (VLIW, кстати). Мучали этот процессор на рынке аж 20 лет. Вот только несмотря на миллиарды потраченных долларов, эта новая "замечательная" архитектура так и не зашла потребителю - ни частникам, ни бизнесу. Производство Itanium закрыто. Процессор признан крупнейшим фейлом Intel за всю историю их существования (пытались скрестить ежа с ужом).
У вас есть больше денег, чем "Intel-овские миллиарды"?
У вас есть 10 лет на разработку и 20+ лет на продвижение?
Вопросы риторические ;-)

Действительно, зачем люди что-то изобретают ? Зачем Райт делал какой-то аэроплан, когда и на лошадях можно доехать ?

И конечно если у интел что-то не получилось, то это всё... приговор на веки вечные что ни у кого не получится.

А если серьёзно, то это статья - изложение моих мыслей о том какие идеи могли бы быть применены в альтернативной архитектуре ЦП. Если кто-то захочет их усовершенствовать - Welcome !

Зачем Райт делал какой-то аэроплан, когда и на лошадях можно доехать ?

Главный-то вопрос не зачем, но как. Вот ключевой отрывок из книжки:

Было решено изучить всю имеющуюся на тот момент литературу о воздухоплавании и познакомиться с опытом американских и зарубежных инженеров. Уилбор и Орвилл решили, что пока они полностью не разберутся в устройстве планеров и законах воздухоплавания, они не станут совершать рискованные испытательные полеты, дабы не повторить судьбу Лилиенталя. Вскоре братья пришли к выводу, что все предыдущие полеты планеров, разработанных американскими и зарубежными инженерами, были неудачными, потому что до сих пор не удалось решить проблему управления и сохранения равновесия. Следовательно, нужно было разработать новую систему управления и решить проблему с сохранением равновесия.

Братья соорудили свою “этажерку”, так как чётко знали что за проблемы они и решают и понимали, что “узкое место” — именно там. Проведя сотни опытов и набрав статистику.

Вы же пытаетесь что-то такое “творить” особо не разобравшись в том что ж вы такое творите, зачем, и, главное, не выяснив что является проблемой, а что — нет.

У интел были и risc архитектуры и нет их, значит они хуже x86?

Ага, именно так. Intel поняла, что на x86/x64 может сделать сильно больше денег и отказалась от RISC - "ничего личного, только бизнес". Потом эпический фейл с VLIW (Itanium-ом). Думали сделать процик по-принципу "и швец, и жнец, и на дуде игрец", но получилось "пятое колесо телеги".
Кстати, я не имею ничего против RISC. Архитектура, как архитектура - ест мало электричества и встроить её можно во всякое.

0) примеры в студию. Не хочется учить верилог/вхдл/нмиген/писать эмуль, сделай хотя бы набросок асм программы с комментами что как на каком ядре/процеоне считается. Чтобы мы посмотрели и поняли - это решение проблем о которых мы не думали и х86/risv-v/арм не справятся

1) 4гб фактически доступного пространства в параграфе это мало. Причём зачем это ограничение сделано - не ясно. Простоц Arr(i) += arr(arr(i)) уже в один параграф не влезет при sizeof arr > 4gb. Или если arr - указатель и у компилятора нет информации о размере и потому придётся обезопаситься.

2) регистров меньше чем в x64(rax-r15+куча xmm+системная мелочь типа gs), не говоря arm и risc-v

3) sufficiently smart compiler требовался итаниуму. Учитывая его яркое пршлое, зачем недостаток писать в достоинство непонятно.

4) "Адрес следующего параграфа может быть получен заранее, до окончания выполнения текущего параграфа" не понял этот момент. это как в случае "Jmp rand()==42? Rand() :fread()" и зачем если ему результат прошлого потребуется(что приводит нас к branch prediction от которого ушли) ? Или более реалистичный пример - jmp (r9 + r10 * 8), который разруливает switch/case'ы. Как без r10 узнать результат?

5) в процессорах деление/доступ к памяти и так не выполняются за 1 цикл. То есть разницы в этом плане нет между процеонами и существующими цпу.

6) Толку от процеонов немгого, если ядро будет вечно селекторов ждать прежде чем дальше двигаться. С таким же успехом можно считать на самом ядре.

7) "Благодаря разделению процессора на сравнительно одинаковые блоки" наоборот. Одинаковые блоки - ядра у современных процов(уродцев big.LITTLE не считаем). Здесь они разные по дизайну: "Процеоны (а значит и линейки команд !) могут быть разными"

8) "Операции чтения из других адресов (оказавшихся в виртуальной памяти) могут быть отложены" это я тоже не понял. зачем их читать если они не нужны? Если нужны, то они не могут быть отложены.

Tldr. Есть ощущение что уходим от старого чтобы к нему же вернуться, но на костылях.

Почему все так зациклились на производительности процессоров? Есть ведь еще такая штука, как "пропускная способность памяти"- 12 ядер Ryzen 3950х сложно нагрузить под завязку обработкой 16GB данных. Эти ядра жрут по 10-12GB/s флоатов каждое, а память позволяет прокачать только ~60GB/s, шесть ядер забивают всю полосу памяти, остальным просто не достается данных. нужны ли мне в таком случае большие толпы процеонов?

ИМХО все зациклились не на производительности процессоров, она-то достаточная обычно.
А производительность однопоточного кода и энергоэффективность.

Райзен это проц для домашнего сектора. Берите лучше серверные решения, где и каналов 8, и память не только DDR обычная может быть.

Спасибо что прочитали всю статью ! Но наверно я все-таки не достаточно популярно объяснил в ней идею процеонов.

Попробую на примере:
Есть код :
a=a/15+58;
if (r>0) {....}

Он выполняется одним параграфом, но двумя процеонами параллельно в виде 2 линеек команд. Примерно так :
{
DATA: RD0=15;RD1=58;RD2=&a;RD3=&r; RD4=&label1 // линейка данных
INT64: MOW RT2, [RD3]; IF (RT2<=RT0) MOW NextCommand,RD4; //Вторая строка кода (Линейка1)
INT64: MOW R1,[RD2]; DIV R1,RD0; ADD R1,RD1; store [RD2],R1 // Первая строка кода (Линейка2)
}

Поясняю :
Линейка1 по результатам сравнения помещает в Регистр Следующей команды адрес метки перехода, а параллельно выполняется Линейка2 (причем команда store не ждет пока данные запишутся, главное чтобы они оказались в памяти к началу следующего параграфа).
Ядро получает адрес в Регистр Следующей команды ДО окончания Линейки2 (команда DIV выполняется несколько тактов) и может уже загружать следующий параграф.

В итоге всё равно суперскаляр с OoO победит, т.к. имеет все преимущества «процеонов» и менее требователен к компилятору.

Папка John Hennessy уже три года назад рассказал куда все это катиться ( https://www.youtube.com/watch?v=Azt8Nc-mtKM&t=1802s ). А катиться оно (инновации в процессора строении) к доменным архитектурам, для каждой задачи свое спецовое ядро. Например, в новом проце Intel Mount Evans за сетевую подсистему отвечает отдельное ядро, которое, кстати, программируется на языке P4.

Не только за сетевую. Тренд сейчас это вынести задачи с хостового проца на DPU. Тонны компаний их сейчас клепают, в их числе интел и нвидия. Выносят тут и сеть, и криптографию, и безопасность, и хранилище. Все, что получится.

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

Начало отсчета это код и данные. Сначала определяются задачи решаемые процессором. Из этих задач получаем код и данные. После этого делаем архитектуру.

Идеальный случай - линейный код и широкие/параллельные данные (GPU/SIMD) больше разрядности процессора. Худший случай - иф на ифе и ифом погоняет и узкие данные меньше разрядности процессора (universal CPU). Посередине куча специфиных кейсов (различные Accelerators/DSP). Приходим к тому, что эффективный процессор это набор различных вычислительных ядер под конкретную работу объедененных одной шиной и неким блоком управления.

Ну и естественно нужен язык программирования на котором можно было бы все это внятно писать :)

Это называется SoC и уже существует. big.LITTLE CPU, GPU, DSP, MCU в одной упаковке.

Вам не кажется, что ваше "ядро" выполняет функции конвейера?

И не только конвейера, еще коммутатора обращений к памяти ( MMU ), управлением задачами и распределением нагрузки.

Я к тому, что может оно уже так примерно и работает в современных процессорах, просто называется конвеером, контроллером DMA, и ещё как-то. Не знаю, кто и как заполняет, например, кэши L1 и L2, но вряд ли это делает основное ядро, верно?

Отмечу разрабатываемую архитектуру ForwardCom: https://forwardcom.info/

Я не специалист, так что вот мой кривой перевод основных фич:

  1. Не RISC и не CISC

  2. Масштабируемость

  3. Автоматическое высчитывание циклов на уровне ассемблера

  4. Без TLB

  5. Без dll/so. Единый формат библиотеки и executable.

Там ещё куча всего интересного.

Что VLIW, что эта идея основаны на том, что в программе мало ветвлений, большие куски кода выполняются линейно и в момент компиляции можно построить план выполнения линейного куска и раскидать его по вычислительным модулям.

Это верно для «числодробительных» задач, где обсчитываются трёхэтажные формулы. Но такие задачи хорошо ложаться на SIMD, GPU, и думать как их оптимизировать в CPU нет практического смысла, разве что для разминки мозгов.

Для типичных enterprise-задач (где фасад проложен над мостом и обмазан интерфейсами, а всё выпуливается из фабрик), поток исполнения прыгает между всеми этими классами, и больших линейных кусков в принципе нет.

Для типичных computer-science задач (списки, сортировки, деревья/графы, парсеры, компиляторы, оптимизаторы) тоже нет больших линейных кусков. Плюс там большая зависимость по памяти от только что прочитанных данных, т.е. нет ресурса для автоматического распараллеливания компилятором/процессором, только переписывание на параллельные алгоритмы (что вне компетенции процессоростроителей).

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

Спекулятивное выполнение можно запланировать на уровне компилятора. Предположим есть код:
if (3*a > 255) b=b/10+1; else b++;
Компилятор это разбивает на 3 линейки вычисляющиеся параллельно, а в конце просто выбирается нужный результат.

Ваш пример и есть «числодробительный» код.
Если брать компиляторы/парсеры, там на горячих местах будет что-то типа
if (node->kind == NodeType::BIN_EXPR) {
    result_node = new Node(node->left, node->right);
} else if (node->kind == NodeType::UN_EXPR) {
    result_node = new Node(node->body);

причём все new — это вызовы malloc/HeapAlloc из runtime/OS и поэтому никак не заинлайнятся. Удачи проспекулировать это средствами компилятора

Спекулятивное/внеочередное исполнение не обладает возможностями ментального вызова функций с неизвестными аргументами для выделения памяти и мгновенным обращением к свежесозданному объекту в следующей итерации

Но оно может заранее попытаться подгрузить node->left, node->right, node->body прямо до проверки node->kind , от типа которого в том числе может зависеть и есть ли вообще эти поля как таковые. В том числе проделать это сразу для нескольких итераций путем раскрутки цикла в коде или компиляторе.

Спекулятивное исполнение может начать уже выполнять пролог ф-ции malloc, не загрузив ещё kind, left, right.

Про раскрутку циклов в этом примере вообще не стоило говорить.
Нет такой глубины спекулятивного исполнения, чтобы полностью накрыть внутренности этого if, да ешё уйти на вторую итерацию.

А может и не начать

Компания Google сообщила о внедрении патчей KPTI и retpoline для блокирования атак Meltdown и Spectre на своих Linux-серверах, в том числе обслуживающих поисковую систему, Gmail, YouTube и Google Cloud Platform. Несмотря на то, что теоретически данные патчи приводят к возникновению дополнительных накладных расходов при выполнении системных вызовов, влияние на производительность обоих патчей при реальной нагрузке Google при выполнении большинства задач оценивается как незначительное. Напомним, что в синтетических тестах наблюдалось проседание производительности до 30% и даже до 60%.

https://www.opennet.ru/opennews/art.shtml?num=47864

В то же самое время есть пример эльбруса в котором malloc можно подгрузить в дополнительный конвеер сильно заранее и вызвать за один такт когда понадобиться. Может это немного и не то по скорости, все таки пока не переключишься, никакие загрузки оттуда вперед не запустятся, но уж всяко быстрей заплаток безопасности и в статике прекрасно планируется. Хотя реализация на эльбрусе не позволяет например два вызова подготовить и поочередно их запустить, можно только подготовить один, вызвать и только после этого можно начать готовить второй.

Про раскрутку я говорил что "в коде или компиляторе" не во время исполнения естественно.

В то же самое время есть пример эльбруса в котором malloc можно подгрузить в дополнительный конвеер сильно заранее и вызвать за один такт когда понадобиться
Эм, я не понял эту фразу. Имеется ввиду, что код заранее загрузить в кеши, чтобы к моменту выполнения не ждать чтений? Это немного не то, можно считать, что весь горячий код уже в кешах.
Про раскрутку я говорил что «в коде или компиляторе» не во время исполнения естественно.
Естественно, я про компилятор. Я говорил о том, что распараллелить какой-то цикл его раскруткой можно только если он короткий, несколько инструкций. А если цикл хотя бы 50 инструкций, то «следующая итерация» начнётся на +50 от текущей инструкции. Слабо представляю, как одновременно может исполняться 50+ инструкций, чтобы говорить, что 2 итерации цикла выполняются параллельно за счёт раскрутки.

Нет, не в кэши, а в конвеер. Устроено это примерно вот так:

      0         5     8
pipe2 | | | | | X
pipe1 | | | | | X
pipe0 | | | | | | | | |
pipe3 | | | | | X

Основной конвеер на девять (или больше) стадий, но в дополнение к нему есть еще 3 обрубка на 6 стадий. Обрубка в том смысле что в них код не исполняется никак, а лишь заполняется кодом функции или бранча на 6 (широких) команд и останавливается. Подготовка конвеера запускается командой

disp, %ctpr1, malloc

после чего в любом удобном месте процедуры можно вызвать

call %ctpr1

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

если цикл хотя бы 50 инструкций, то «следующая итерация» начнётся на +50 от текущей инструкции

Ну даже так может оказаться полезно вынести загрузку данных первой итерации из цикла, а в теле цикла разместить обработку этих данных вместе с загрузкой данных для следующей, как бы "открутив" от него.

Интересно, а если в начале malloc тоже есть какой-то call, то компилятор при создании бинарника основной программы, ничего не зная про вызываемые внешние функции, тут уже ничем не сможет помочь (если оставаться в парадигме классического c/c++, а не прибегать к jit. да и с jit предвижу большой пласт проблем в переходах между генерируемым кодом и внешним библиотечным). Интеловский же подход это всё прожуёт и не подавится.

Короткие циклы можно раскрутить на процеонах, как я описывал в статье.

Предположим есть код:

char k=64;
................ //возможно здесь k изменился
for (i=0;i<k;i++) sum+=a[i]*b[i];

В этом случае компилятор создает параграф типа-2, в котором будет только ОДНА линейка команд, но параллельно выполняющаяся на нескольких процеонах. А результат просуммируется в конце.

Скалярное произведение — это и есть «числодробительная» задача, которую сейчас неплохо решает SIMD.

Тогда любой цикл без вызова внешних функций - "числодробилка".

Нет, не любой. К не числодробилкам я отнесу всякие xml/json/css-парсеры. Или игровую логику, где программа поведения юнитов имеет очень запутанную сеть ветвлений и чтений памяти по косвенным индексам. То есть всё то, что не имеет линейных кусков по 20-30 инструкций, которые можно отдать процеонам.

Хорошо, вот пример на ветвления. Пусть будет обычный CASE на десяток вариантов (или многоуровневый IF-ELSE). В CISC,RISC и VLIW это выльется в десяток последовательных сравнений с переходами. Причем спекулятивное исполнение им не сильно поможет, параллельно можно запустить 2-4 варианта, не больше. Процеоны же позволяют параллельно запустить сравнения по всем вариантам и выдать правильный адрес перехода.

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

Вы плохо читали статью. Попробую поподробнее.
Есть CASE

switch (a){
case 25: {.....}
case 44: {.....}
case 49: {.....}
case 55: {.....}
case 70: {.....}
case 72: {.....}
case 90: {.....}
}

Как это выглядит в коде параграфа для процеонов (предполагаем что а уже в R1):

{
DATA: RD0=25;RD1=44;RD2=49;RD3=55;RD4=70;RD5=72;RD6=90; RD7=&label1 ; RD8=&label2; RD9=&label3; RD10=&label4; RD11=&label5; RD12=&label6; RD13=&label7
INT64: IF (R1==RD0) MOW NextCommand,RD7; 
INT64: IF (R1==RD1) MOW NextCommand,RD8;
INT64: IF (R1==RD2) MOW NextCommand,RD9;
INT64: IF (R1==RD3) MOW NextCommand,RD10;
INT64: IF (R1==RD4) MOW NextCommand,RD11;
INT64: IF (R1==RD5) MOW NextCommand,RD12;
INT64: IF (R1==RD6) MOW NextCommand,RD13;
}

В NextCommand попадет только результат одной линейки. Или вообще ничего.

Плохой пример. Это надо же столько электричества сжечь и столько кода потратить из-за такой мелочи. Настройка регистров, линеек, запуск, ожидание синхронизации. Когда в x64 это просто
        mov     rax, QWORD PTR jmptab[rax*8]
        jmp     rax

А если в rax окажется 2**32 ?

Понятно, что тут нужна ещё одна проверка, но на суть примера мало влияет.
В идеале, было бы круто взять какой-то реальный код, например из PCRE и посмотреть, ляжет ли на процеоны хоть что-нибудь оттуда.

А вот и попробуйте что-нибудь подобрать, попробуем вместе придумать как это можно закодить для процеонов.

Прочитал по диагонали до самого главного — память и кэши. И тут мне кажется все остальные умозаключения теряют смысл. Процеоны не имеют своих кэшей, ходят постоянно в ядра. Т.е. мало того, что все обращения к памяти будут проходить через шину между процеонами и ядрами, так еще здесь же будет постоянно гулять трафик для поддержания когерентности кэшей. Как кэши вообще эти устроены? Один общий пул для всех ядер? Несколько уровней? Вся производительность в этом месте и умрет. Сначала дизайн начал напоминать Cell, где точно так же разделены управляющие и вычислительные ядра. С той лишь разницей, что там все вычислительные ядра имеют свою память и сидят на кольцевой шине, что разгружает управляющие ядра от необходимости еще обслуживать между ними взаимодействие. Все это в итоге выглядит как VLIW — без компилятора, который идеально разложит все обращения к памяти, это все работать не будет.

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

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

Как кэши вообще эти устроены? Один общий пул для всех ядер? Несколько уровней? Вся производительность в этом месте и умрет.

Кэши двухуровневые. L1 - на ядро, L2 - на процессор. Задачи должны как можно реже мигрировать между ядрами, тогда в кэше L1 будут нужные данные.
Именно для разгрузки обращений в память сделаны отложенные операции чтения/записи. А непосредственное чтение возможно только из заранее зарезервированной области.

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

И да и нет. Это не VLIW - процеонная схема гораздо гибче (в теории) и позволяет выделять переменное количество ресурсов в зависимости от нагрузки. Но увы, компилятор действительно нужен более сложный.

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

Проблема будет, но будет решаться на стороне L1 и L2. Тогда это все начинает напоминать недавнюю статью habr.com/ru/post/581222 Ваша архитектура походит на попытку всунуть OoO во VLIW. Может проще уже признать, что RISC/CISC лучше и приложить свой гений там? Все эти трюки там уже и так реализованы, в том или ином виде, хоть в x86, хоть в arm.

Именно для разгрузки обращений в память сделаны отложенные операции чтения/записи. А непосредственное чтение возможно только из заранее зарезервированной области.

Разгрузка эта будет достигнута ценой постоянных конфликтов параграфов между собой, их перестановкой и, в конечном итоге, pipeline stalls. Я вижу здесь туже самую проблему VLIW — невозможность достичь высокой загрузки блоков реальной работой. У вас явно идеализированное представление о том, как реальный код будет себя вести. В реальном коде полно зависимостей по памяти, ветвлений и, наконец, атомарных операций. И компилятор с этим ничем не поможет. Как вы вот предполагаете атомарные операции откладывать? Это нарушит их сериализуемость. А атомарные операции нынче повсюду.

Ваша архитектура походит на попытку всунуть OoO во VLIW.

Не так. Я размышлял о том как переложить OoO на плечи компилятора. Процессор в момент выполнения жестко ограничен ресурсами, в отличии от компилятора, который имеет больше свободного времени для разбора кода.

Разгрузка эта будет достигнута ценой постоянных конфликтов параграфов между собой, их перестановкой и, в конечном итоге, pipeline stalls.

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

Как вы вот предполагаете атомарные операции откладывать? Это нарушит их сериализуемость. А атомарные операции нынче повсюду.

Атомарность записи обеспечивается только внутри параграфа.

А атомарные операции нынче повсюду.
Автор скажет «мало ли что повсюду принято, а у нас так не делают. Переписывайте без атомиков». Атомик будет очень дорогой операцией, с ценой одного параграфа (ведь только они последовательны).
Sign up to leave a comment.

Articles