Как стать автором
Обновить

Комментарии 87

Давайте проясним один момент. Внутри процессора x86 нет RISC-составляющей. Это просто маркетинговый ход.


Папа-компьютерщик из Осло(подпись автора), видимо не в курсе, что RISC-ядро появилось в x86 уже в Pentium Pro.

Все так просто?
RISC-подобные микроинструкции в P6, конечно, имеются. Но сама философия RISC (https://www.gartner.com/en/information-technology/glossary/risc-reduced-instruction-set-computer) стремится исключить микрокод (в RISC-V как раз для упрощения аппаратной реализации (экономии мультиплексоров) для некоторых операндов весьма замороченный порядок битов (кто возился с RISC-V эмуляторами, поймет :-). Недаром для RISC-V такое внимание уделяется тестированию чуть ли не всех возможных битовых комбинаций в командах.
Но дальнейшее развитие CISC-процессоров пошло как раз в направлении реализации все большего количества CISC-инструкций аппаратными блоками (расхожий пример — цена AESENC — 1 uops (https://www.agner.org/optimize/instruction_tables.pdf)).
Но есть и контрпример — в VIA C3 можно было добраться до инструкций RISC-ядра (https://en.wikipedia.org/wiki/Alternate_Instruction_Set).
Хотя и в самом RISC-V есть некоторое отклонение от "линии партии", а именно, многообразие расширений RISC-V. Причем многие из них можно эмулировать стандартным RV32(64)I, что на первый взгляд нарушает "no instruction or addressing mode whose function can be implemented by a sequence of other instructions should be included in the ISA", но есть удобная оговорка "unless its inclusion can be quantitatively shown to improve performance" (https://www.realworldtech.com/risc-vs-cisc/).

Все так просто?

Не все просто, но то, что современные x86 risc это да.
Поэтому
Вероятно, вы слышали, что M1 — процессор с архитектурой ARM, а ARM — это RISC, в отличие от Intel и AMD.

вводит как минимум в заблуждение.

Второй момент, что реализация Risc у всех разная, отсюда у нас всякого рода атаки на память.
ARM — это довольно условный RISC, в V8 там и операции с 2-мя таргетами и апдейтом, встроенные сдвиги, возможность неатомарного исполнения некоторых инструкций и т.д.
Если быть совсем точным, то впервые RISC ядро на х86 появилось у AMD в их K5. И это было прямо отражено в документации на процессор.
Да. К5 родился, как известно, из 29k, которые были весьма продвинутыми процессорами в то время. Да что там говорить — он имел 4 декодера, у более поздних процессоров это появилось только после 2006.
Небольшое добавление по этой холиварной теме «RISC vs CISC», и их взаимной конвергенции назло пуританам.
В комментариях к статье "ZX Spectrum из коронавируса и палок (на самом деле, не совсем)" этот вопрос, что и откуда растет, достаточно полно обсуждался. Я же повторю еще раз цитату "RISC это не технология, RISC это стратегия дизайна. Когда то RISC был бунтом против норм, сейчас он — норма"
Надеюсь что расставленные точки над i, не вызовут очередного витка противостояния.

Кстати, интересно а этот Alternate Instruction Set где нибудь подробно описан, или задокументирован? Ну что его можно было использовать? К примеру насколько сложно было бы перенести к примеру Linux? И насколько был бы VIA C3 быстрее в родном режиме? Точнее сколько ресурсов съедает интерпретация x86 в нативные RISC инструкции?
Он даже цитирует одного из разработчиков этого Pentium Pro. В следующем же предложении.

Там цепочка ссылок с цитатами, собственные слова этого Боба Колуэлла можно прочесть тут: www.quora.com/Why-are-RISC-processors-considered-faster-than-CISC-processors/answer/Bob-Colwell-1
M1 быстрее не потому что он RISC, а потому что VLIW

потому что в прошлом веке ещё была такая альтернатива — или сложные многотактовые команды, или сокращенные однотактовые.
Сейчас такой альтернативы нет — все команды на современных процессорах выполняются за 1 такт. что CISC, что RISC

А вся борьба за производительность идет на уровне производительности чтения из памяти.
Ключевая проблема архитектуры х86 — это переменная длина команды

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

Эта проблема скорости не процессора, а внешних, по отношению к процессорному ядру, устройств.
обсуждение скорости выполнения команд CISC/RISC относится исключительно к процессорному ядру.
Сама команда загрузки выполняется за 1 такт. + время ожидания готовности памяти

дальнейшее выполнение программы не может быть продолжено пока не команда не завершилась успешно. Соответственно фактическое время выполнения команды load будет 1 такт если значение есть в кеше либо же 1 такт + время ожидания значения из основной памяти. Вот это время ожидание это момент когда процессор фактически простаивает и может заняться чем то другим. Out-Of-Order execution может применяться для выполенения других команд которые не зависят от ожидания значения из памяти.
Либо же можно выполнять команды из другого потока (Hyper Threading)

Сейчас такой альтернативы нет — все команды на современных процессорах выполняются за 1 такт.

Это вряд ли. Как минимум ряд инструкций из AES-NI (например, aesdec), насколько я знаю, выполняются отнюдь не за 1 такт — latency там может быть от 4 до 8 тактов в зависимости от конкретной архитектуры, даже если оба операнда — регистры.
M1 даже не рядом с VLIW. Хоть погугли что пишешь :)
habr.com/ru/post/538812
очень-очень рядом :)))))
там где-то в середине статьи приводится пример команд, где покороче у х86 и подлиннее у арм :)
из-за чего в интеле 4 декодера, и то с трудом, а у м1 восемь, без особого труда :)))
По вашему, разница между VLIW и RISC/СISC только в числе декодеров? Более странной аргументации не встречал.
То что длинна команд может в СISC большая быть, не делает его VLIW.
из-за чего в интеле 4 декодера, и то с трудом,

А ничего что в Skylake 5 декодеров, причем 1 из них сложный декодер, который способен 1-4 микрооперации давать?

VLIW — qualcomm hexagon dsp. Вот одна инструкция:
{ R17:16 = MEMD(R0++M1)
MEMD(R6++M1) = R25:24
R20 = CMPY(R20, R8):<<1:rnd:sat
R11:10 = VADDH(R11:10, R13:12)
}:endloop0

Я очень хочу процессор, который все команды выполняет за один такт (и тактов с секунду много). А то как только деление f64, так всё, пошло муржить.

Так в х86 уже давно придумали, конвейер!) Просто шли инструкции потоком и будут тебе твои f64/такт

Вы сильно переоцениваете производительность float-point деления. Вот результаты бенчмарка. Это мой рабочий ноутбук примерно 4х-летней давности.


Вот код бенчмарка:


use criterion::{black_box, criterion_group, criterion_main, Criterion};

pub fn fdiv(c: &mut Criterion) { 
    c.bench_function("fdiv", |b| b.iter(
        ||{
            let mut base: f64 = 1e308;
            for x in 0..1000{
                    base = black_box(base/11.11);
                }
            }
    ));
}

criterion_group!(bench, fdiv);
criterion_main!(bench);

Что тут видно? 1000 тел цикла выполняется за 8.74us, т.е. 8.7 ns на каждую операцию. Каждая операция внутри — это inc, cmp, jmp и, собственно, fdivl. Мой процессор работает на частоте 2.40GHz, т.е. один тик — это 416ps. 8.74ns/0.416 — это 20 тиков. Как вы думаете, сколько из этих тиков приходится на inc/cmp/jmp, и сколько на fdivl? Я даже не уверен, что там есть jmp, или там внутри честный loop unrolling.


Я полез искать — со времён K7 не сильно всё поменялось. Там тоже обещали 11-25 тиков latency для fdiv.

Поэтому стоит избегать деления, лучше заменить его на 10 MULADD или как-то еще обойти.

Ну вот у меня есть 2D геометрическая область, заданная парой точек (итого 4 точки). Мне нужно её разделить на NxN областей (можем на случае 2х2 сфокусироваться).


Как?


Сейчас код дуболомный:


    pub fn split(&self) -> [Self;4]{
        let len_x = (self.end.x - self.start.x) / 2.0;
        let len_y = (self.end.y - self.start.y) / 2.0;
        [
            Self::from_coords(self.start.x, self.start.y, self.start.x + len_x, self.start.y + len_y),
            Self::from_coords(self.start.x + len_x, self.start.y, self.end.x, self.start.y + len_y),
            Self::from_coords(self.start.x, self.start.y + len_y, self.start.x + len_x, self.end.y),
            Self::from_coords(self.start.x + len_x, self.start.y + len_y, self.end.x, self.end.y),
        ]
    }

(from_coords — конструктор для Boundry, которую я и режу в split).
Как мне вот этот /2.0 убрать?

Умножить на 0.5 — очевидно же.

А вот вопрос — будет ли оно быстрее.


1000 делений на 2.0 — это 443.17 ns
А вот умножения на 0.5:


fdiv          time:   [442.59 ns 442.84 ns 443.11 ns]                 
              change: [-0.0256% +0.0292% +0.0863%] (p = 0.30 > 0.05)
              No change in performance detected.

Не видно разницы.


Код:


pub fn fdiv(c: &mut Criterion) { 
    let mut base = [1e308f64;1000];
    c.bench_function("fdiv", |b| b.iter(
        ||{
            for i in 0..1000{
                    base[i] = black_box(base[i]*0.5);
                }
            }
    ));
}

… а вот без конвейеризации


2.5865 us -> 2.5861 us


Всё равно нет разницы. Умножение и деление — либо хорошо оптимизированы для простых случаев, либо считаются за одинаковое время.

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

Вы помните, что это floating point? Я не уверен, что умножение на 0.5 сильно быстрее деления. Кстати, а какой алгоритм умножения для f64?


Насчёт миллиардов — это criterion. Он сделал больше 5 миллионов итераций функции (1000 чисел) для статистически значимых результатов. Если он говорит, что "разницы нет", значит нет.

Там, скорее всего, компилятор уже подставляет умножение — это элементарная оптимизация. Ассемблерный код в студию.

Это потрясающая задача. Ассемблер бенчмарка — больше 10 мегабайт текста (сам бенчмарк имеет много интересных опций, таков уж criterion). Я попытаюсь завтра ответить на ваш вопрос, но мне придётся сначала научиться раст генерировать код в отдельные объектные файлы и т.д. Задача интересная и полезная...

Я попытаюсь завтра ответить на ваш вопрос

Получилось?
Кстати, если это 64-битный режим, то по умолчанию SSE — то есть если вообще деление осталось, то будет divsd, а не fdiv (и, возможно, векторизованное).

Ой, я и забыл про исходный тредик. Я научился смотреть ассемблер, избавился от criterion (т.к. от него ассемблера слишком много из-за макросов), научился заставлять компилятор выносить нужную мне функцию в отдельную функцию no matter what, разобрался с феноменами деления и умножения (да, деление на степени двойки — это у множение на соотв. числа, умножение на 2 — сложение), написал статью на медиум по этим вопросам https://medium.com/journey-to-rust/viewing-assembly-for-rust-function-d4870baad941


А в тредик описать забыл, извините.

Тем не менее, для деления на 2.0: godbolt.org/z/3cqEe3
Хмм, это интересно, я думал, деление никогда не заменяется на умножение. Видимо в простых случаях это все таки возможно.

Интересно, если я black_box вокруг 2 использую, оно замаскирует?


… Замаскирует. Потрясающе.


change: [+87.133% +87.144% +87.156%] (p = 0.00 < 0.05)
Performance has regressed.


сравнивал не-пайплайновое:
base[0] = black_box(base[0]*black_box(0.5));
и
base[0] = black_box(base[0]/black_box(2.0));


А вот пайплайновое (умножение vs деление).


change: [-41.503% -41.452% -41.407%] (p = 0.00 < 0.05)
Performance has improved.


Офигеть. Самый полезный тред за этот месяц — сколько узнал...

умножение деление Разница есть.

Возможно criterion использует не слишком точный таймер, да и вообще, мерять 0.5ms даже с QPC — не слишком перспективное занятие. Нужно увеличить количество итераций.

Нет, criterion очень точный и делает очень много итераций, а вся история свелась к тому, является ли цифра 2.0 black box'ом или нет (т.е. может ли компилятор оптимизировать его).


https://habr.com/en/company/selectel/blog/542074/?reply_to=22687892#comment_22689838

Нууу… Когда я кодил на verilog FPU, то сильно удивился что самая медленная операция это… сложение\вычитание, а деление не сильно медленнее умножения. Если я правильно помню, в той фигне, что я делал, было так — (~200МГц)
сложение\вычитание — ~10-15 тактов
умножение\деление — ~5-8 тактов
Но это именно для fp32(или fp24, не помню уже) и он был не конвейерный. Есть всякие оптимизации которые больше «транзисторов» съедают, но я делал просто в лоб.
сложение\вычитание — ~10-15 тактов
умножение\деление — ~5-8 тактов

Не в обиду, но это ни о чём не говорит. Тут много нюансов. Вы ведь не перебирали пространство решений на поиск самой эффективной реализации? Если интересно, у Xilinx есть корочка Floating Point, где можно поиграться с кол-вом тактов, максимальной частотой и оптимизациями по ресурсам для операций с плавающей точкой.

Это я к чему, далеко не факт, что ваша реализация сойдется по таймингам или ресурсам для случая того же процессора, а если не сойдется, тогда что толку в 5 тактах деления?

"но я делал просто в лоб" — я написал, да, можно у фпу разными выкрутасами уменьшить такты на команду, но мне было интересно посмотреть банальные реализации по стандарту.
Тут какб кто как сделает. Но суть что не всё так очевидно, и деление может оказаться не таким уж и медленным.)
Зы Сижу на Altera там тоже есть параметрезируемые… Модули, но они как-то не интуитивно работают, настроил циферки, чота получил, а как оно работает ток боги знают (иди читай документацию на IP)

Посмотри на GPU supersampling

Я не совсем понимаю куда смотреть. Там описывается кусок работы с изображением, а мне нужны геометрические операции на (имитации) ℝ2. Как из одного другое сделать — я не совсем понимаю.

Как вы думаете, сколько из этих тиков приходится на inc/cmp/jmp, и сколько на fdivl?

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

Спасибо за замечание. Я как раз и пытался сделать конвейер (ради этого 1000 была добавлена), но ступил. Сейчас проверю без зависимости.


            let mut base = [1e308f64;1000];
            for i in 0..1000{
                    base[i] = black_box(base[i]/11.11);
                }
            }

fdiv              time:   [1.8966 us 1.8984 us 1.9004 us]                  
                  change: [-78.313% -78.259% -78.204%] (p = 0.00 < 0.05)
                  Performance has improved.

(почесав в затылке я вынес let за пределы цикла, стало 1.6867 us)
Итого: 1.69ns на операцию, 4 тика. Если считать, что цикл — это 1 или 2 тика, мне делают fdiv за 1-2 тика. Сильно быстрее.


Я был не прав, оно хорошо конвейеризируется.

И уж добивая вопрос, на моём AMD 5600X тот же бенчмарк выдал 969.74ns. Что транслируется в 970ps на операцию, при частоте 3.7GHz это 270ps на тик, т.е. 3.5 тика на операцию. (Вот он прогресс процессоров за последние 10+ лет — с 4.1 тика до 3.5 тика).

Деление и не будет быстрым — оно делается итеративно, по Ньютону-Рафсону.

В соседнем треде меня убеждают, что может (с учётом конвейеризации). Получается 3.5 тика на операцию (вместе с циклом).

Есть процессоры где от деления оставляют только вычисление начального приближения, а дальше «по Ньютону-Рафсону». Но это катит только в том, случае, когда у вас есть плавающая точка шире чем double. Тогда вы вычислите результат деления быстро и точно. Иначе, если хотите точный результат, то тактов будет побольше чем в обычном делении. Отсюда, во многих RISC процессорах (пример ARM,RISC-V) оставляют обычные CISC-команды деления и корня, а уже разработчик процессора сам решает делать их многотактовыми или однотактовыми.
Просто то что я помню, изучая асм, а конкретно FPU, у х86 то, что он полностью конвейерный, и если ты положишь туда штабелями 20 умножений то через те же 20 тактов их всех и заберёшь, а если в разрез этих инструкций что-то положить, то и время когда это всё забирать увеличиться на столько же. Проблема что это ручками надо оптимизировать или компиляторы специальные брать, по дефолту многие не умеют.
Ну тогда у него будет очень малое количество тактов в секунду. Ведь физику транзистора не обманешь — всё упирается в скорость срабатывания логического ключа (читай транзистора).

Транзисторы умеют только складывать и сдвигать биты в регистрах.
Разные математические операции раскладываются в разное количество последовательного выполнения этих примитивов.

Я первый раз слышу про транзисторы, которые умеют складывать. Обычно нужна нереальная куча транзисторов даже для невинного XOR'а. Но это совершенно не отменяет возможности иметь аппаратную акселерацию сложных инструкций (в т.ч. за счёт ALU, которое бегает быстрее остального процессора).

Ок, я упростил про транзисторы.
Так а как оно будет бегать «быстрее остального процессора»? Одни транзисторы будут бегать быстрее других или как-то волшебно распараллелить? Так вот деление устроено так, что там особо не параллелизуется (как и синусы, корни etc).

Блок ALU, отвечающий за деление, работает на большей частоте, чем остальной процессор. Например, на 10-кратной. Процессор — на частоте в 3Гц, конкретная небольшая (пара десятков тысяч) транзисторов — на 30Ггц. Ещё можно иметь несколько блоков деления, когда каждое деление долго, но за счёт конвейеризации, аммортизированное время выполнения быстрее.


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


(И да, я подумываю о CUDA, ибо на проце медленно).

>> Блок ALU, отвечающий за деление, работает на большей частоте, чем остальной процессор. Например, на 10-кратной. Процессор — на частоте в 3Гц, конкретная небольшая (пара десятков тысяч) транзисторов — на 30Ггц.

пруф?

>> Ещё можно иметь несколько блоков деления, когда каждое деление долго, но за счёт конвейеризации, аммортизированное время выполнения быстрее.

так разговор изначально был про «любая мат. операция за такт» =)

>> В целом, если видеокарты могут быстро делить, то и процессоры могут, было бы желание.

не могут)

Я говорю, что мне нужно иметь fpu, который (аммортизированно) считает 1 операция за такт. Исходный комментарий был о том, что мол, шли свои инструкции в конвейер и будет тебе 1 инструкция за такт (а я показал бенчмарком, что это очень не так).


Меня устраивает вариант "20 fpu операций за 20 тактов", если мне про это не надо будет думать (а компилятор справится).

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

деление f64 это обычное целочисленное деление 53-разрядных чисел. Самый простой вариант деления это один бит за такт. Но тогда 53 такта. Можно чуть увеличить аппаратуру и получать 2 бита за такт — уже в 2 раза быстрее. Если хотим очень быстро делить, то стараемся за такт получить как можно больше бит результата, но тут (как уже написал кто-то) важно не превысить критическое время цикла. Нашли максимальное количество бит за такт — далее строим конвейер. В итоге можем получить деление за один такт. Главное чтобы конвейер не слишком длинный получился :)
деление f64 это обычное целочисленное деление 53-разрядных чисел.

Нет, далеко не всегда и не везде. Почитайте про Goldschmidt method — как минимум у AMD именно он. Про современный Intel я точных данных не слышал — во времена FDIV bug был SRT, который, да, целочисленный. Но с тех пор дети выросли и сами работают....


но тут (как уже написал кто-то) важно не превысить критическое время цикла

Совершенно не важно. Если известно, что блок деления выдаёт, например, 7 бит за время 3 тактов, поставят делитель тактовой на 3 — это совершенно банально. Если не скупиться, на каждую такую задачу есть несколько вариантов готовых блоков, как её сделать (для деления, например, можно пакован разных вариантов SRT заранее разработать) и выбирать, какой будет эффективнее на пару процентов, чем альтернативы.

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

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

Далее, если же вы всё же какую-то мелкую операцию сможете заакселерировать быстрее такта основного процессора, то тут вам результат придется перевести в более медленную частотную область основного конвейера и этот перевод потребует нескольких тактов, так что в итоге всё равно проиграете.
Если что, деление u64 или i64 тоже нифига не быстрое и однотактное.
не потому что он RISC, а потому что VLIW

У VLIW не бывает OoO, так что мимо.

Ключевая проблема архитектуры х86 — это переменная длина команды

На х86 есть кэши микроопов, CISC декодеры там большую часть времени вовсе стоят.
Сейчас такой альтернативы нет — все команды на современных процессорах выполняются за 1 такт. что CISC, что RISC

Это, мягко говоря, не правда: https://www.agner.org/optimize/instruction_tables.pdf
Можете убедиться, что даже целочисленное умножение занимает 3 такта.


Ключевая проблема архитектуры x86 (ИМХО) в том, что ради повышения производительности не за счёт повышения тактовой частоты пришлось наворотить огород с внеочередным выполнением команд. Например, в kaby lake 97 микро-операций может находиться в reservation station, а какие именно операции туда положить решает branch predictor, в котором чуть ли не нейросети принимают решение. А каждая микро-операция там — это теневая копия регистров. После спекулятивного исполнения на стадии retirement её пытаться встроить так, чтобы все думали, что там in-order исполнение было. Если вдруг какие-то зависимости между командами были — то результат внеочередного исполнения придётся выкинуть и попробовать сделать ещё раз. И вся эта массивно-параллельная конструкция спрятана за абсолютно линейным ISA, который ещё и даёт строгие гарантии на memory order.


Зато можно запустить говно мамонта, где-нибудь их 80-х. Почему Интел за это держится? Ну, как минимум они уже один раз попробовали с Itanium.


image

Алгоритм Томасуло с внеочередным исполнением, допустим, успешно применяется с самых 60-х, а в высокопроизводительных микропроцессорах — с 90-х. Да и в сабжевом М1 он тоже в каком-то виде реализован. Преимущества динамического планирования исполнения вполне очевидны.
VLIW же в Итаниумах показал себя с настолько плохой стороны, что пришлось делать нормальный OoO бэкенд.

Работал в конторе, которая делает софт для всяких телекомов — это как раз те самые ребята, которые покупали Itanium. И там даже не с производительностью проблемы были (переносили не с x86, а со всяких проприетарных платформ типа HP-UX), а с тем, что надо было не просто пересобирать всё, а код переписывать, под модель памяти Itanium, в которой есть типы.

Ключевая проблема архитектуры x86 (ИМХО) в том, что ради повышения производительности не за счёт повышения тактовой частоты пришлось наворотить огород с внеочередным выполнением команд.

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

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

И всё это спрятано от компилятора, хотя он мог бы очень даже хорошо тут помочь.

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


Сейчас уже нет. ThunderX2 (в девичестве Vulkan) имел SMT4 (как и Netlogic XLP, из которого он переделан). ThunderX3 должен был продолжить эту традицию, но что-то пошло не так.

это одна из причин, почему ряд производителей высокопроизводительных чипов ARM, таких как Ampere, выпускают 80-ядерный процессор без гипертрединга


Причина там, в первую очередь — экономия транзисторного бюджета, а также тот факт, что Ampere собирается наставить ядер N1
«Сегодня объяснить разницу между этими наборами команд сложнее, чем во время их появления».
Очень точно сказано. Во время появления, технологические нормы — ??? мкм, средства проектирования — кульман+карандаш с резинкой :) Аппаратный умножитель был экзотикой. Куча алгоритмов реализовывались так, что только бы не использовать умножения :)
Вот тогда слово «Reduced» действительно что-то значило.
Нравятся мне эти переводные рассуждения:
«Но чтобы действительно понять разницу между RISC и CISC, вам нужно избавиться от этого мифа… Мысль о том, что внутри CISC-процессора может быть RISC, только запутает вас… Давайте поговорим о том, что из себя представляют RISC и CISC. И то, и другое — философия того, как нужно проектировать процессоры….Было принято решение сделать внутренности CISC-процессора более RISC-похожими….Фактически некоторые RISC-процессоры используют микрокод для некоторых инструкций, как CISC-процессоры.»

В статье точных определений RISC и CISC не приводится. Но мысль крутится вокруг того, что обе архитектуры частично позаимствовали философию обработки команд друг у друга. И в текущем моменте у каждой есть достоинства и недостатки, как и нет безоговорочного лидера.
Второй важный момент заключается в лицензировании. Apple не может свободно создавать свои процессоры с набором команд x86. Это часть интеллектуальной собственности Intel, а Intel не хочет конкурентов.

x86-compatible processors have been designed, manufactured and sold by a number of companies, including:


x86-processors for regular PCs
  • Intel
  • AMD
  • VIA
  • Zhaoxin

In the past:
  • Transmeta (discontinued its x86 line)
  • Rise Technology (acquired by SiS, that sold its x86 (embedded) line to DM&P)
  • IDT (Centaur Technology x86 division acquired by VIA)
  • Cyrix (acquired by National Semiconductor)
  • National Semiconductor (sold the x86 PC designs to VIA and later the x86 embedded designs to AMD)
  • NexGen (acquired by AMD)
  • Chips and Technologies (acquired by Intel)
  • Texas Instruments (discontinued its own x86 line)
  • IBM (discontinued its own x86 line)
  • UMC (discontinued its x86 line)
  • NEC (discontinued its x86 line)
Эльбрус сюда же — в поддерживающие нативное исполнение х86.
Пусть частично аппаратно, а частично эмуляцией.

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

Вот даже не могу сейчас вспомнить, кто бы сегодня проектировал процессор и назвал бы его архитектуру CISC. Может кто-нибудь приведет примеры?
Типовой подход проектирования (на примере ARM, RISC-V):
1. вначале простенький RISC
2. затем + расширения
3. далее всё что влезет, т.к. все помнят только п.1 :)
Надо изучать. Мне кажется, по разному все пляшут.
ISA может быть как в Форте у Технофорт ТФ-16, например.
У Мультиклета и Мальта, что-то своё изобретено…
ТФ-16 это вообще RISC of the RISC :)
У Мультиклета интересная идея, но подход к ISA скорее всего как у RISC,
т.е. набор команд попроще, все в один такт.
Про Мальта ничего не слышал. Может дадите какую-нибудь ссылку.
Malt
Научный парк МГУ, судя по адресу.
Спасибо. Почитал — как свежего воздуха вдохнул ;) Архитектура Malt это уже разговор о строении, а не о том, что лучше: кирпичи или газосиликатные блоки. Буду с интересом следить за этими разработками.
А я чего-то вспомнил одного конструктора:
Истинный RISC может быть только троичным.
Тогда уж и самосинхронным!
У ARM есть преимущество в виде команд reg0 := reg1 op ~reg2 shift const
Что даёт некоторое ускорение по сравнению с х86, в последних х86 появилась такая фича как слияния инструкций, это когда две или три простых инструкций можно слить в одну и выполнить в одном из конвейере, вероятно за один такт. Что позволяет конкурировать с ARM процессорами. Старые х86 типа Athlon II x4 640, уже не могут конкурировать с современными(не очень) ARM процессорами, даже при тех же 3 инструкции за такт.
И ещё, в х86 разных фичь конечно больше.
>У ARM есть преимущество в виде команд reg0 := reg1 op ~reg2 shift const
Это, AFAIR, повыкидывали из v8

Нет, доступно для основных операций.

В итоге то, за кем будущее?
arm победит amd64?
Я при своей жизни увижу, как все датацентры выбросили все серваки и все люди поменяли ноуты на arm?
У эпл получится пропушить эту идею, или всё сдохнет, как с клавиатурой бабочкой?
Просто я из статьи понял, что у cisc нет будущего и всё это было временно как хрущёвки)
В итоге то, за кем будущее?

Есть мнение, что за специализированными вычислителями. Процессоры общего назначения уже не дают достаточного выхлопа на популярных задачах, поэтому будут добавляться сопроцессоры, акселераторы и всё такое прочее для конкретных типов задач (в принципе, это можно наблюдать уже и сейчас, когда в пределах одной SoC совмещаются процессорные ядра, ядра GPU, FPGA и аппаратных кодеков обработки видео).
На самом деле, там мешанина из разрозненных фактов и субъективщины. CISC у них не конвейеризуется в т.ч. на уровне микроопов, зависимости между операциями только по памяти, х86 до сих пор 32-битный, а «сжатые наборы инструкций» — только на RISC. Плюс сумбур по поводу микроопов/микрокода — складывается впечатление, что автор не до конца понимает, что это такое. Странные утверждения про RISC/не RISC внутреннее устройство CPU — так, например, у AMD микроопы называются RISC86 с давних времен. Суперскалярные архитектуры не рассмотрены вовсе — рассматривается какое-то старье времен i386.

Я при своей жизни увижу, как все датацентры выбросили все серваки и все люди поменяли ноуты на arm?

Нет предпосылок для этого. В свое время х86 стабильно предлагал лучшее отношение цена/производительность, обратную совместимость, и широкий набор софта — RISC'ам нечего предложить здесь.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.