All streams
Search
Write a publication
Pull to refresh
69
0.9
Иван Савватеев @SIISII

Микроконтроллеры, цифровая электроника, ОС…

Send message

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

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

Сейчас -- из-за очень сложного декодера, о чём выше было сказано, но в сравнении есть определённое лукавство. Если будем сравнивать потребление энергии на выполнение какой-то элементарной команды (скажем, сложение), то при прочих равных у ARM оно будет сильно ниже. А вот если какое-нибудь шифрование нескольких килобайт данных... Современный AMD64/Intel 64 делает это, насколько помню, одной командой (IBMовские мэйнфреймы -- точно одно командой), на ARM же придётся шифровать программно, и кто меньше сожрёт в таких условиях -- уже большой вопрос, а если учесть ещё и потребное время на такую операцию...

Пы.Сы. А нанометры нынешние -- это маркетинг, к реальным размерам транзисторов они давно уже (где-то с рубежа 40 нм) отношения не имеют вообще.

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

это не баг, а именно фича. Технически NEG это "инвертировать все биты и добавить 1". А поскольку инверсные значения битов уже есть (у триггеров в регистрах есть и прямой, и инверсный выход), то NEG сводится к инструкции INC, применённой к инверсному выходу регистра аргумента.

NEG -- это изменение знака, а не инверсия, а инверсия -- это NOT:

Так что это не фича, а именно что баг (собственно, Интел где-то это признала, насколько помню).

не заменяет, но значительно ускоряет программную эмуляцию. Прям в разы. Скажем, сложение и вычитание это именно сдвиги — перед, собственно, целочисленным сложением мантисс их нужно выровнять друг относительно друга (чтобы совпадала "точка").

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

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

И что самое смешное, barrel shifter позволяет реализовать целочисленное деление быстрее, чем его выполняли аппаратные делители 80-ых годов. В частности, борландовские компиляторы для х86 вместо инструкций деления использовали деление сдвигам

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

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

«Битность» процессора, например 8-битный или 32-битный, определяет, какой объем данных он может обрабатывать одновременно

Некорректно, если не добавить важное уточнение: с точки зрения программиста. Разрядность внутренних блоков, путей данных и т.д. и т.п. может быть любой. Скажем, сверхпопулярный в своё время Z80 имеет 4-разрядное АЛУ, хотя для программиста это 8-разрядный процессор. ЕС-1020, ЕС-1022, ЕС-1030 и ЕС-1050 реализуют одну и ту же архитектуру (32-разрядную IBM System/360), но у первой АЛУ 8-разрядное, у второй -- 16-разрядное, у третьей вообще дикая смесь всего (скажем, сумматор комбинация из 32- и 4-разрядного), а у четвёртой -- 64-разрядное, ну и так далее.

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

Ничего парадоксального: либо ты сразу формируешь широкий адрес (кстати, у ранних АРМов он был, насколько помню, 26-разрядным), либо должен городить какую-то дополнительную аппаратуру для преобразования логических (виртуальных) адресов в физические. Правда, в конце концов всё равно городить пришлось, чтоб обеспечить поддержку виртуальной памяти, но это произошло намного позже (вроде, только в ARMv5).

Команда, состоявшая из менее чем десяти человек, создала сложнейший 32-битный RISC-процессор, который заработал безупречно с первой же попытки.

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

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

...

В 1986 году увидела свет вторая версия процессора — ARM V2. В ней появилась поддержка сопроцессоров (например, для вычислений с плавающей запятой) и встроенные аппаратные схемы для операций умножения.

Аппаратное умножение отсутствовало даже в некоторых вариантах ARMv4, а деление появилось вообще лишь в ARMv7-M (а у микропроцессоров -- линейки -A -- оно отсутствует до сих пор; считается, что достаточно иметь FPU, который у -A есть, кажется, всегда).

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

Скриншот из первой версии Принципов работы Системы 360, A22-6821-0.

Возможно, Вы спутали с тем, что сама защита ключами, как и интервальный таймер, в Системе 360 была необязательной (хотя де-факто присутствовала во всех "настоящих" моделях, кроме 30 и, может, 40 -- но и там была возможной, просто за отдельную плату).

Проблема в том, что Кобол так и не ушёл. Да и мэйнфреймы живее всех живых :)

Ну а синтаксис Це/Це++ был ужасным изначально, и дальше его лишь делали ещё более ужасным :(

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

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

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

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

на которые каналам S/360 было наплевать

А вот тут Вы запамятовали: защита памяти ключами действует на каналы точно так же, как и на процессор, так что, если программа канала выполняется с ненулевым ключом (он указывается в CAW в момент запуска канальной программы), то залезть в чужую память не сможет.

что дезавуирует сам термин в такой его трактовке

Скорей, подтверждает мертворождённость самой изначальной идеи микроядерной ОС.

Я думаю, Вы согласитесь, что само по себе это на самом деле не принципиально для рассуждений о микроядре

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

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

Если же отвлечься от конкретно макоси и прочая, то, думается, с приемлемыми накладными расходами микроядро можно было бы реализовать за счёт использования сегментации IA-32 или механизма адресных пространств ESA/370/390/z/Architecture, где очень многие вещи делаются на уровне железа, без необходимости программного вмешательства. Но сама идея мне представляется мертворождённой: она не может обеспечить подлинную надёжность, ведь падение, скажем, подсистемы управления потоками не позволяет просто загрузить её заново с диска и запустить: при падении могут быть похерены критически важные данные (или, скорее, само падение произошло из-за того, что они уже были похерены из-за какой-либо ошибки в коде), а соответственно, и надёжный перезапуск без полной перезагрузки системы не представляется возможным. Даже падение драйвера не всегда позволяет разумным образом его перезапустить (скажем, нельзя "принимать на веру" находящиеся в очередях запросы ввода-вывода и прочие управляющие данные, касающиеся этого драйвера). В общем, для упрощения отладки -- вещь полезная (легче ловить выходы за границы логически доступной памяти), для реального повышения надёжности -- в общем случае бесполезная (хотя и полезная в определённых конкретных случаях: скажем, держать компилятор шейдеров внутри драйвера видюхи, выполняемого в режиме ядра -- ну, такое себе решение).

Вот только куар-коды для этого -- так себе идея. Как по мне, лучше всего классический виндузовый БСОД с распечаткой базовой информации прямо в текстовом виде.

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

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

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

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

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

Ну, мне Ваш вариант неизвестен вообще и я так и не понял, в чём его суть :)

Микроядро реально не спасает в случае серьёзных сбоев: если упало что-то критически важное, то просто взять и перезапустить его нельзя, ведь причиной падения вполне могут стать кривые данные этого самого критически важного -- а их инициализировать заново, опять-таки, нельзя, поскольку они критически важны для работы системы (скажем, очереди запросов ввода-вывода, списки процессов и потоков и т.п. вещи). В реальности микроядерных систем, как это понималось изначально, вероятно, не существует. У той же QNX от изначальной идеи микроядра остался лишь вынос драйверов в отдельные процессы; все остальные функции традиционного ядра ОС (управление процессами, потоками, памятью, синхронизация и т.д. и т.п.) де-факто находятся в одном адресном пространстве и не защищены друг от друга. Но вынос драйверов или их частей возможен и в Винде; более того, часть драйверов и работает сейчас в пространстве пользователя. Скажем, бОльшая часть кода, относящегося к ГПУ, уже не часть ядра, и сбои в нём приводят лишь к падению определённых приложений (игрушка, например, вылетит), а не всей системы в целом.

У ГПУ, если не всегда, то обычно есть встроенные железные блоки для декодирования видео -- т.е. они это делают чисто аппаратно, а не программно, что и быстрей, и намного энергоэффективней. Есно, возможно и программная перекодировка, но она нужна, скажем, для какого-то нового алгоритма, не поддерживаемого в железе.

Если же говорить про чисто программную перекодировку, ГПУ с этим справляется лучше по скорости просто из-за того, что эта задача почти всегда очень хорошо параллелится. Обычные процы по своей натуре чисто последовательные, и наличие всяких дополнительных СИМД-команд лишь несколько увеличивает их "параллелизм" (и то, при условии, что программа умеет их использовать: толку от очередного новомодного SSE100500/AVX200600, если кодек был скомпилирован под базовую систему команд IA-32 (x86) без каких-либо SIMD-расширений?), в то время как ГПУ изначально созданы для параллельной обработки.

Качество же кодирования с видом проца прямо не связано -- оно зависит от конкретного алгоритма, используемого для кодирования. Если алгоритм абсолютно идентичен, то и результат будет абсолютно идентичен. Другое дело, что для ускорения или уменьшения объёма данных могут использовать определённые упрощения, допуская при этом некоторую потерю качества. Можно вспомнить, например, картинки JPEG, алгоритм кодирования которых позволяет играться с качеством результата и степенью сжатия изображения (чем меньше результат -- тем ниже качество). Но это, повторюсь, не от вида процессора зависит, а от особенностей алгоритма.

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

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

Во-первых, в большинстве популярных CPU x86 adc просто выполняется медленнее, чем обычная add. Так как у adc есть третье входящее значение (флаг переноса), эта команда сложнее, чем add

Абсолютно неверное утверждение. Команды вида ADD и ADC выполняются с абсолютно одинаковой скоростью и на абсолютно одном и том же оборудовании. Технически разница между ними заключается только в том, что в качестве входного переноса в ADD подаётся нуль, а в ADC -- значение переноса, запомненное при выполнении какой-либо предшествующей команды.

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

Information

Rating
1,769-th
Location
Солнечногорск, Москва и Московская обл., Россия
Date of birth
Registered
Activity

Specialization

Embedded Software Engineer
Lead