Иван Савватеев @SIISII
Микроконтроллеры, цифровая электроника, ОС…
Information
- Rating
- 1,760-th
- Location
- Солнечногорск, Москва и Московская обл., Россия
- Date of birth
- Registered
- Activity
Specialization
Embedded Software Engineer
Lead
Микроконтроллеры, цифровая электроника, ОС…
Разве select и poll -- это асинхронный ввод-вывод, а не неблокирующий? Я не линухоид, поэтому и спрашиваю. Просто неблокирующий к асинхронщине отношения не имеет и толку с него в этом плане примерно нуль (смысл асинхронного ввода-вывода, как правильно указано в статье, -- сообщать о завершении операции ввода-вывода, ну а неблокирующий лишь говорит о том, что файл готов к дальнейшей работе -- а, скажем, настоящий честный дисковый файл всегда готов).
Он никогда корректен не был. Скажем, Система 360 была анонсирована в 1964-м, первые модели поступили в продажу в 1965-м -- и её 16 регистров общего назначения были таки "общего назначения". Правда, в трёх экзотических командах использовался один жёстко определённый регистр (в Системе 370 число таких команд возросло до шести), но подавляющее большинство команд, в том числе все "нормальные", используют регистры на равноправной основе. Про PDP-11 выше тоже сказали; правда, указатель стека и счётчик команд относятся и адресуются как регистры общего назначения, хотя таковыми, по сути, не являются -- но то же самое относится и к ARM, например.
В общем, сей "признак" характерен либо для 8-разрядных микропроцессоров, либо для исключительно уродской системы команд 16-разрядного 8086/8088, но не для других современных последнему 16-разрядных микропроцессоров, например, Zilog Z8000.
Безусловно. Ни первой конвейерной, ни первой суперскалярной S/360 не была; вот внеочередное выполнение команд, кажется, впервые реализовали таки на одной из моделей этого семейства в самом конце 1960-х. Но, возможно, IBM стала первой, кто все эти вещи стал внедрять в относительно массовых вычислительных системах, а не в единичных исследовательских проектах или супер-ЭВМ.
Ну, если свалить в Китай, которому пофиг на американские законы... Другое дело, что там почти наверняка будут ничуть не меньшие ограничения, только в другую сторону, а оно им надо?
В V86 он войти не может, насколько помню: поддержка V86 выпилена из 64-разрядного режима. Хотя за IA-32/AMD64 уже много лет не наблюдаю, так что могу и ошибаться.
Но ничто не мешает 64-разрядному коду переключаться в 32-разрядный режим, где тот же V86 благополучно поддерживается из-за совместимости.
Примерно то же самое. Правда, ещё и электроника, да и ассемблеры были другими (8086 -- примерно пятый мой ассемблер, от которого я дико плевался, да и сейчас плююсь).
Нет, основной код -- как раз на сях; на питоне -- вспомогательный скрипт, без которого можно обойтись.
Но настоящая проблема не в том, что молодёжь питон использует, а в... скажем так, узколобо-фанатичном подходе и в крайне узком кругозоре, характерном для очень многих. Тот же ИИ отнюдь не на питоне сделан, вся лежащая в его основе математика написана на других языках, вплоть до фортрана и ассемблера. Но из всех утюгов несётся: питон, питон, питон! (А 10 лет назад неслось: руби, руби, руби!)
Ну, хоть кто-то в 16 лет сейчас интересуется ассемблером и прочими низкоуровневыми вещами, а не сплошными быдлосайтиками или, в лучшем случае, ИИ на пыхтоне :)
Из минусов -- приличное кол-во ошибок в русском языке. Стоило б подтянуть.
Целый ряд неточностей, да и выводы сомнительны. Пройдусь по некоторым вещам.
Для начала -- о терминологии CISC и RISC -- это не архитектуры, это, если угодно, способы организации архитектур, но не сами архитектуры. И IA-32 (x86), и System/360, и PDP-11, и VAX-11 -- это всё CISCи, но совершенно разные, т.е. разные архитектуры. То же самое касается и RISCов.
Придерусь и к ISA, т.е. к instruction set architecture. Это -- не очень удачное определение, так как затрагивает только одну сторону вопроса, ведь архитектура -- это не только набор команд. Архитектура определяет и ряд других вещей, например, способы обработки прерываний. Можно, скажем, взять систему команд от VAX-11, но прицепить к ней обработку прерываний от M-профиля архитектуры ARM -- и что тогда получится? Это ведь будет уже и не VAX, и не ARM.
Насчёт конвейеризации. CISC, в общем и целом, на конвейер ложится ничуть не хуже RISCа. В частности, среди первых моделей Системы 360 старшая была уже конвейерной; из первых четырёх ЕС ЭВМ, которые архитектурно тоже были Системой 360, старшая -- ЕС-1050 -- была конвейерной, ну и т.д. Более того, в 1960-х появились и первые суперскалярные процессоры, и даже процессоры с внеочередным (out-of-order) выполнением команд. Просто тогда это был удел буквально нескольких машин во всём мире, а сейчас такое чуть ли не в каждом утюге встречается -- но это является следствием развития микроэлектроники.
Далее, начсёт частоты использования команд и их влияния на производительность. Да, простые команды используются намного чаще, и это было неправильно воспринято некоторыми как возможность "безнаказанно" избавиться от сложных команд. Результатом стал, в частности, MIPS. На сравнительно небольшом промежутке времени -- в 1980-90-х -- это, действительно, позволило добиться превосходства в производительности над вычислительными системами аналогичных массо-габаритных характеристик и себестоимости, но причина крылась в другом: тогдашние технологии уже позволяли впихнуть простой процессор в одну микросхему, но не позволяли сделать этого для сложного процессора (или позволяли, но только с чисто микропрограммным управлением, без конвейера или с очень простым конвейером). Соответственно, MIPS, будучи выполнен в виде одной микросхемы, мог работать на высокой тактовой частоте и при этом реализовывать конвейерное выполнение команд, а какой-нибудь 80386 -- не мог. Вот топовый мэйнфрейм рвал по производительности и 80386, и MIPS, как тузик грелку -- только его не поставить было не только на стол, но даже и в небольшую комнату.
Но так было до тех пор, пока не появилась возможность впихнуть сложный процессор с конвейером и всем прочим на один кристалл. И постепенно CISCи, в общем и целом (а не на специализированных задачах -- это важно!), снова вырвались вперёд. Причина кроется, в частности, в низкой плотности кода, характерной для RISCов: тактовые частоты процессоров росли куда быстрее, чем пропускная способность и особенно латентность памяти. Конечно, наличие кэшей снижало остроту проблемы, но ведь и CISCи умеют пользоваться кэшами, при этом, из-за более высокой плотности кода, им нужен куда меньший кэш команд.
Более того, наличие команд для реализации достаточно сложных алгоритмов, скажем, вычисления хэшей и прочего шифрования, поднимает производительность на этих задачах иногда в десятки раз. Причина здесь не только в исключении лишних выборок и декодирования простых команд (выбрана и декодирована одна сложная команда -- и процессор побежал её выполнять), но и в том, что процессор, выполняя такую команду, точно знает, какие доступы к памяти данных ему потребуются, в каком порядке они будут идти и т.п. -- а значит, может максимально оптимизировать обращения к данным, чего в обычной программе добиться невозможно -- а это критично для высокой производительности из-за уже упомянутой пропускной способности и латентности памяти. (Кстати, для программной оптимизации современные процессоры содержат команды-подсказки, дающие процессору возможность заблаговременно подгрузить данные, которые потребуются в будущем -- но это лишь суррогат).
По этим причинам в современных RISCах от изначальной идеологии RISC мало что осталось. Скажем, современные ARMы сохранили лишь невозможность прямой обработки данных в памяти -- их требуется загружать для этого в регистры. Однако у них переменная длина кода команды, переменное время выполнения команды, а сам полный набор команд насчитывает не одну сотню (для сравнения: у Системы 360 полный набор включал всего лишь 143 команды). В общем, в нынешнем ARMе наличествуют почти все черты типичного CISCа. Конечно, кодирование команд в ARM многократно проще, чем в IA-32 (x86), но последняя -- пример того, какой не должна быть система команд, а особенно -- её кодирование; она отвратительна со всех точек зрения. А вот команды Системы 360 или, скажем, PDP-11 декодировать ничуть не сложней, чем команды ARM, -- хотя они являются самыми что ни на есть настоящими CISCами.
Ну и насчёт сложности процессоров. Процессор какой-нибудь ЕС-1020 или System/360 model 30 ощутимо проще, чем даже жутко примитивное ядро Cortex-M3 M-профиля архитектуры ARM, не говоря уже о всяких там Core i-100500. Основную сложность создаёт не система команд как таковая и даже не конвейер, а реализация суперскалярного и особенно внеочередного выполнения команд. Дурное кодирование команд в IA-32 резко усложняет задачу их декодирования (попробуй, хотя бы, определи длину команды!), но после декодирования, по большому счёту, особых сложностей с выполнением команд нет. Ну и ничто не мешает реализовывать простые команды чисто аппаратным управлением, а сложные -- аппаратно-микропрограммным: какое-нибудь микропрограммное шифрование будет выполняться всяко быстрее, чем тот же самый алгоритм, реализованный чисто программными средствами, а дополнительных аппаратных затрат требует мизер.
Когда как. Скажем, если благодаря ручному написанию на ассемблере удастся уместиться в одну строку кэша, скорость иногда может возрасти в десятки раз -- хотя это патологический случай, конечно, но полностью исключать подобное было бы неправильно. Про зависимости по данными Вы сами написали. Плюс, программист, в отличие от компилятора, знает, что и когда действительно нужно; когда можно забить на "волатильность" данных, а когда нет, ну и т.д. -- а это может избавить от кучи лишних телодвижений. Понятно, что для ПК это почти всегда некритично, а вот на микроконтроллерах может иногда быть важным.
Ну, я неоднократно убеждался, что я всегда способен написать ощутимо более компактный код, чем даёт любой компилятор, и нередко (но уже не всегда) более быстрый. Другое дело, что пытаться сэкономить каждый бит и каждый такт обычно не требуется -- но случаи разные бывают, как известно.
Вообще, это мешает переносимости: то, что работает на одной реализации архитектуры, может не работать на другой. Проблема в том, что, кажется, лишь IBM в своей Системе 360 и её потомках озаботилась дать исчерпывающие сведения о поведении машины как в нормальных, так и в "ненормальных" условиях (в том числе точно оговорила, на что можно, а на что нельзя полагаться, если нужно сохранить переносимость -- в общем, специфицировала случаи UB, только они, в отличие от С/С++, являются разумными и достаточно очевидными: скажем, если какой-то бит зарезервирован, программист должен сохранять его равным нулю, и тогда при будущих расширениях архитектуры ничего не сломается).
Если изучил один асм, перейти на другой будет весьма и весьма просто.
Ну, принципиальной разницы между асмом и машинным кодом нет -- в отличие от между асмом и любым языком высокого уровня.
На самом деле, UB тоже может иметь место :) В частности, насколько помню, команды сдвига в 8086 в некоторых случаях не совсем так же работают, как в более поздних процах (но только в экстремальных случаях -- скажем, когда счётчик числа сдвигов превосходит разрядность; но подробности за давностью лет не помню). Но по сравнению с сишным UB... :)
Интересно, с чего это высокий порог входа? Он весьма и весьма прост. Разве что многословен -- это да.
Чем меньше возможностей для случайных ошибок, тем лучше. Тем более, что путаница между 0 и o, c и с, l и 1 нередко приводит-таки к возникновению ошибок при компиляции (скажем, неизвестный идентификатор). Паскаль препятствует возникновению подобных ошибок, C допускает массу случаев, где они возможны.
Ну, я сравниваю, главным образом, чистый C и чистый Паскаль, причём Паскаль появился на пару лет раньше -- но не имеет основной массы проблем, которые имеются в C. Изрядная часть кривизны С++ -- это следствие попыток сохранить совместимость с чистым С вместо того, чтобы выкинуть её полностью и делать нормальный язык с читабельным синтаксисом и строгой типизацией.
Я в последнем абзаце указал, что сейчас почти все проблемы решаются включением всех предупреждений и превращением их в ошибки -- но (1) эта возможность появилась не так уж давно (напомню, чистый С -- самое начало 1970-х, С++ -- 1980-е) и (2) это, по сути, костыль, смягчающий кривизну языка. Собственно, мой пост как раз про то, что С/С++ -- изначально кривые, откуда и возникает множество ошибок на пустом месте.
Проблемы есть и в C/C++ -- эта сладкая парочка просто провоцирует возникновение ошибок на пустом месте.
Например, изначально, ещё в чистом C, регулярно возникали большие проблемы из-за отсутствия логического типа. Такие ошибки, как & вместо && или наоборот, в Паскале невозможны в принципе. (Замечу попутно, что введение bool в C++ ничем реально не помогло, поскольку типизация как была слабой, так и осталась -- сплошные автоматические преобразования без всякого реального контроля).
Ещё одна проблема -- присваивание рассматривается как операция, а не оператор, что приводит к if (a = b) вместо if (a == b).
Можно припомнить и отсутствие настоящего символьного типа, ведь char -- целое число, а не символ. Конечно, технически это одно и то же, но логически-то это разные вещи. Проблема усиливается тем, что char может быть как знаковым, так и беззнаковым, и это никак не регламентировано (например, какой-то древний код, успешно работавший раньше, вдруг может перестать работать, будучи скомпилирован новым компилятором).
А ещё приснопоминаемый break, который нужно явным образом указывать в case...
А ещё до самого 2011-го года не было стандартизации размеров целочисленных типов. Правда, в Паскале этого тоже нет -- но язык создавался как учебный. Вот в Аде есть, правда, оформлено по-другому.
Плюс, запутанный и неудобочитаемый синтаксис. Увидишь * и не можешь сразу, без анализа, понять, что это -- умножение или разыменование указателя? Или int *A[10] -- это массив указателей или указатель на массив? Понятно, что постоянное использование языка смягчает эти проблемы, но читабельность в любом случае хуже паскалеподобных языков, которые куда однозначнее, а заодно не допускают слишком уж извращённых синтаксических конструкций.
Да, сейчас большая часть потенциальных проблем в C/C++ более-менее решается включением всех предупреждений и превращением их в ошибки, но когда-то такой возможности не было; кроме того, подобные костыли нужны для обхода/смягчения изначальной кривизны самого языка. Так что реальная проблема изначально кроется всё ж и в языке тоже, а не только в его "гибкости" -- ту же самую гибкость и функциональность, что имеет C, имеет и классический Паскаль (и точно так же имеет проблемы с утечками памяти или использованием её после освобождения -- поскольку вот эту проблему без ущерба гибкости, функциональности и производительности в полном объёме решить как раз невозможно).