Ipc это в процессорах
Отступление: данная статья является учебной и расчитана на людей, только еще вступающих на путь системного программирования. Ее главный замысел — познакомиться с различными способами взаимодействия между процессами на POSIX-совместимой ОС.
Убийство RISC, эпоха P6
Говорят, что в каждое десятилетие истории Intel вносит большие изменения в свои процессоры, в 80-х именно 80386 обновил ISA до 32 бит, а в 90-х это был Pentium Pro, процессор, который считался лидером. от x86 до рынка рабочих станций, где в то время доминировали архитектуры RISC.
От Intel очень хорошо знали, что набор инструкций x86 имеет ограничения, поэтому Intel создала для этого процессора как неупорядоченное, так и спекулятивное исполнение. Кроме того, было увеличено количество этапов с 5 до 14 и впервые добавлен кэш второго уровня.
- Pentium Pro был первым процессором, в котором кэш-память второго уровня была встроена в процессор, до тех пор он был установлен на плате рядом с процессором.
- Что касается Pentium II, он был основан на Pentium Pro, но он переместил кэш L2, хотя и оставил его в том же корпусе, в отличие от своего предшественника, он был запущен для рынка домашних ПК, принося мощность работы станции.
- Pentium III, с другой стороны, включал инструкции SSE и в конечном итоге в конечном итоге интегрировал кэш L2 обратно в процессор.
Стратегия Intel сработала, и в конце 90-х большинство архитектур RISC томились в ожидании своей окончательной смерти. Только ARM и PowerPC, использовавшиеся в Macintosh, выжили, остальные были вооружены дамокловым мечом и вскоре уступили.
Именованный канал
Примечание: mode используется в сочетании с текущим значением umask следующим образом: (mode & ~umask). Результатом этой операции и будет новое значение umask для создаваемого нами файла. По этой причине мы используем 0777 (S_IRWXO | S_IRWXG | S_IRWXU), чтобы не затирать ни один бит текущей маски.
Как только файл создан, любой процесс может открыть этот файл для чтения или записи также, как открывает обычный файл. Однако, для корректного использования файла, необходимо открыть его одновременно двумя процессами/потоками, одним для получение данных (чтение файла), другим на передачу (запись в файл).
В случае успешного создания FIFO файла, mkfifo() возвращает 0 (нуль). В случае каких либо ошибок, функция возвращает -1 и выставляет код ошибки в переменную errno.
- EACCES — нет прав на запуск (execute) в одной из директорий в пути pathname
- EEXIST — файл pathname уже существует, даже если файл — символическая ссылка
- ENOENT — не существует какой-либо директории, упомянутой в pathname, либо является битой ссылкой
- ENOSPC — нет места для создания нового файла
- ENOTDIR — одна из директорий, упомянутых в pathname, на самом деле не является таковой
- EROFS — попытка создать FIFO файл на файловой системе «только-на-чтение»
Пример
mkfifo.c
Мы открываем файл только для чтения (O_RDONLY). И могли бы использовать O_NONBLOCK модификатор, предназначенный специально для FIFO файлов, чтобы не ждать когда с другой стороны файл откроют для записи. Но в приведенном коде такой способ неудобен.
Компилируем программу, затем запускаем ее:
В соседнем терминальном окне выполняем:
В результате мы увидим следующий вывод от программы:
Наконец, 32 бита, 80386
80386 не был первым 32-битным процессором в истории Intel, но iAPX 432 удостоился чести быть им, однако, когда пришло время создать 32-битную версию 8086, Intel взяла многие концепции из этого проекта, хотя самой большой проблемой было перемещение кода. Решение? Используйте те же регистры, но с расширенным размером до 32 бит. Кроме того, 80386 был первым процессором в истории Intel с сегментированным конвейером, который был трехступенчатым.
Хотя большая проблема была связана с многозадачностью, идея иметь процессор, который может запускать операционную систему, способную запускать несколько программ одновременно на персональном компьютере или рабочей станции. Для этого они разработали новый MMU, на этот раз поддерживающий виртуальную память на подкачку страниц, метод, который используется сегодня.
80286, самый большой скачок IPC по сравнению с процессором x86
Успех IBM PC был ошеломляющим, уже в 1984 году он стал по преимуществу платформой в качестве персонального компьютера для предприятий во всем мире, но было необходимо создать более быструю версию, которая пришла с созданием AT PC и этого раз да, что IBM, полностью доверяя Intel, попросила его сделать новый процессор полностью обратно совместимым на двоичном уровне, но намного быстрее.
80286 был самым большим скачком в IPC, который был сделан в истории процессоров Intel, поскольку его производительность на тактовый цикл более чем вдвое превышала производительность 8086. Для этого были улучшены его внутренние шины, которые остановили их мультиплексирование, процесс оптимизирован захват инструкций и улучшена функциональность инструкций перехода. Кроме того, Intel впервые включила MMU, который работал по сегментам.
Любопытно, что это самый долговечный процессор для ПК в истории, поскольку даже в 1990 году вы могли найти в продаже компьютеры с этим процессором.
Что инструменты мониторинга производительности на самом деле должны показывать
Я считаю, что каждый инструмент мониторинга производительности должен показывать значение IPC рядом с загрузкой процессора. Это сделано, например, в инструменте tiptop под Linux:
Ранняя история Intel, до 8086 года
В первом разделе этой истории мы собираемся обсудить процессоры, которые Intel производила до появления IBM PC, которые привели их к успеху и стать крупнейшим производителем процессоров для персональных компьютеров в истории.
Архитектура против архитектуры, когда речь идет о CPI и IPC
Одно из самых распространенных заблуждений - это сравнение двух архитектур под одним и тем же ISA, но от разных производителей. , что является неверным сравнением, если оно проводится без учета ряда условий.
Поскольку нам совершенно неизвестны исторические изменения, которые каждый производитель вносил в CPI и IPC инструкций, при измерении программы даже под тем же ISA мы обнаружим несопоставимые результаты, если сравним производительность с производительностью другой программы. Причина? Оба они используют одни и те же инструкции, но время выполнения каждой инструкции разное.
Но если мы говорим о реализациях одного и того же производителя, то мы обнаруживаем, что и ЦП, и ГП развиваются постепенно, от одной итерации к другой сохраняется значительная часть инструкций предыдущей и CPI нескольких выбранных в конструкции процессора. период.
Intel 4004, первый процессор на кристалле
Если вы посмотрите любую книгу по истории, вам скажут, что первая ЦП что Гордон Мур и Роберт Нойс, разработанные под брендом Intel, были i4004, который считается первым законченным ЦП, собранным из одной части, поскольку раньше ЦП имел все свои компоненты, разделенные на несколько разных микросхем.
Однако в то время это не был флагманский продукт компании, поскольку ее капитал поступал от продажи памяти DRAM и SRAM для мини-компьютеров того времени. В то время не было персональных компьютеров, а оперативная память была на уровне искусства. Хотя не Нойс и Мур подошли к столу разработчиков, чтобы создать 4004, это была работа трех инженеров: Федерико Фаггина, Теда Хоффа и Стэнли Мазора, которые разработали полный 4-битный процессор для Busicom, компании, занимающейся электроникой. калькуляторы из Японии, заказавшие проект у Intel.
Таким образом, компания, которая в то время была молодой компанией по сравнению с другими гигантами того времени, такими как Fairchild Semiconductor или Texas Instruments, была ответственна за создание первого полного процессора на кристалле.
Контрольные показатели рассказывают только часть истории
Один из способов измерить производительность процессора - это тесты, которые представляют собой не что иное, как программы, выполняющие серию инструкций, и именно в выборе используемых инструкций является ключ ко всему. Производители в значительной степени полагаются на тесты производительности при продаже продукта, и им необходимо знать, какие инструкции в их новых версиях будут использоваться больше всего.
Вот почему есть тесты, которые больше ориентированы на использование инструкций, чей CPI был улучшен в процессоре, или на предоставление преимущества определенной марке над другими, в то же время производители также отдают предпочтение улучшению CPI. и IPC наиболее часто используемых инструкций для тестов производительности.
Эпоха многоядерных процессоров в истории Intel
Первые Intel Core были основаны на архитектуре P6, но с двухъядерной конфигурацией. Однако они разработали второе поколение, которое было революционным и считается лучшим процессором 2000-х годов.
Для своего развития Intel скопировала несколько идей в Opteron от AMD; как и реализация северного моста в процессоре и принятие 86-разрядного расширения x64. Что касается производительности, они почти вдвое увеличили IPC, и это третий по величине скачок производительности после того, как был достигнут с 80286 и 80486. Для этого они улучшили выполнение вне очереди, сделали ЦП способным обрабатывать больше инструкций в параллельно, и они сначала добавили Smart Cache в процессоры Intel.
- Сэнди / Айви-Бридж: Intel снова улучшила блок предсказания перехода, помимо этого улучшила такие элементы, как кэш микроопераций, блоки целых чисел и с плавающей запятой, а также производительность некоторых инструкций для выборки данных из памяти.
- Хасуэлл/Бродвелл: Intel снова увеличила количество инструкций, которые ЦП может выполнять за цикл, помимо увеличения пропускной способности внутренних кешей процессора и улучшения контроллера памяти. Они также включали внутрипроцессорный контроллер напряжения (FIVR).
- Поколение SkyLake: Intel увеличила количество инструкций, которые процессор может декодировать, но не увеличила количество инструкций, которые он может выполнять параллельно. Изменения по сравнению с предыдущими поколениями очень небольшие (убрали FIVR).
- Ракетное озеро-С / Тигровое озеро: Это текущие Rocket Lake-S и Tiger Lake. После нескольких лет небольших улучшений в IPC Intel решила пойти по пути AMD, чтобы не отставать.
Его последний выпуск - Intel Core 12 с архитектурой Alder Lake-S, который добавляет больше новинок с момента запуска Intel Core 2, таких как исполнение с гетерогенными ядрами, добавление Thread Director и других новинок, которые мы не делаем. знайте, будет ли это большим скачком между поколениями, который компания совершает каждое десятилетие, или же под капотом будет что-то еще, ясно то, что история Intel по крайней мере захватывающая.
Intel 8080 и штанга S-100
Снова Федерико Фаггин и Стэн Мазор вернулись к работе, на этот раз с помощью Масатоши Шима, который уже сотрудничал с ними в разработке 4004. Из любопытства, Фаггин покинул Intel в 1974 году, чтобы основать Zilog и создать улучшенную версию в форма знаменитого Z80, который был центральным процессором таких систем, как Spectrum, MSX, Amstrad CPC и многих других.
8080 не был процессором, созданным с нуля, он был построен на основе дизайна 8008. Они сохранили систему прерываний, но улучшили адресацию с максимальных 16 КБ. Оперативная память до 64 КБ, добавлены дополнительные периферийные порты и новые инструкции. Что касается тактовой частоты, она выросла с 0.5 МГц до 2 МГц, что в четыре раза быстрее, чем у предшественника.
Этот ЦП выделялся тем, что являлся основным процессором Altair 8800, созданного Массачусетским техническим институтом, который принес с собой первое поколение персональных компьютеров под названием S-100 из-за того, что различные компоненты компьютера были подключены к общей плате. который использовал одноименный интерфейс. Операционной системой для машины MITS была CP / M, созданная Гэри Килдаллом, предшественником MS-DOS.
Благодаря использованию в стандартной комплектации шины S-100 клоны Altair 8800 вскоре появились повсюду, но, к сожалению, существование улучшенного Z80 оставило Intel вне лидирующей позиции этих предшественников персональных компьютеров.
Вместо заключения
В следующих статьях я хочу рассмотреть технологии d-bus и RPC. Если есть интерес, дайте знать.
Спасибо.
UPD: Обновил 3-ю главу про семафоры. Добавил подглаву про мьютекс.
Во время поколения процессоров в последние годы и за исключением AMDВ Threadripper все процессоры имеют практически одинаковый размер, почти неизменный, за исключением нескольких миллиметров. Если производственные узлы получат все меньше и меньше , почему не размер процессоров тоже сжимается? В этой статье мы собираемся объяснить, почему процессоры такого размера, ни больше, ни меньше, а также возможные исключения.
Как вы знаете, процессоры, также известные как ЦП, подобны «мозгу» ПК. Вся информация, которая выполняется и обрабатывается, проходит через миллионы транзисторов, скрытых под кристаллом, и поскольку каждый раз, когда производственным узлам удается уменьшить эти транзисторы, нормально думать, что они также могут уменьшить размер процессоров, и но это не так.
Какая связь между FLOPS и CPI и CPI?
FLOPS - это количество операций в запятых или с плавающей запятой, которые может выполнять единица выполнения, операция длится только один цикл, и ее не следует путать с инструкцией. Они также используются в маркетинге вместо CPI и IPC, особенно когда речь идет о графических процессорах.
Например, у нас может быть процессор, который имеет гораздо более низкую скорость FLOPS, чем другой, и получит лучшую производительность из-за того, что количество циклов на инструкцию меньше в одной архитектуре, чем в другой . Когда дело доходит до измерения производительности в FLOPS, отделы маркетинга следуют инструкциям с самым низким CPI из всех, если они представляют очень высокие числа. Но на самом деле все мы знаем, что программа состоит из множества разрозненных инструкций.
Точно так же, как ошибочно сравнивать разные архитектуры с разными CPI, еще более ошибочно сравнивать разные архитектуры с точки зрения скорости FLOPS.
Та метрика, которую мы называем «загрузкой процессора» на самом деле многими людьми понимается не совсем верно. Что же такое «загрузка процессора»? Это то, насколько занят наш процессор? Нет, это не так. Да-да, я говорю о той самой классической загрузке CPU, которую показывают все утилиты анализа производительности — от диспетчера задач Windows до команды top в Linux.
Вот что может означать «процессор загружен сейчас на 90%»? Возможно, вы думаете, что это выглядит как-то так:
А на самом деле это выглядит вот так:
«Работа вхолостую» означает, что процессор способен выполнить некоторые инструкции, но не делает этого, поскольку ожидает чего-то — например, ввода-вывода данных из оперативной памяти. Процентное соотношение реальной и «холостой» работы на рисунке выше — это то, что я вижу изо дня в день в работе реальных приложений на реальных серверах. Есть существенная вероятность, что и ваша программа проводит своё время примерно так же, а вы об этом и не знаете.
Что это означает для вас? Понимание того, какое количество времени процессор действительно выполняет некоторые операции, а какое — лишь ожидает данные, иногда даёт возможность изменить ваш код, уменьшив обмен данных с оперативной памятью. Это особенно актуально в нынешних реалиях облачных платформ, где политики автоматического масштабирования иногда напрямую завязаны на загрузку CPU, а значит каждый лишний такт «холостой» работы стоит нам вполне реальных денег.
Дорога к Intel 8086
Существует мнение, что Intel 8086 был процессором, созданным Intel по запросу IBM для своего первого ПК, но это неверно, поскольку он уже существовал два года назад и его история не имеет ничего общего с первым персональным компьютером в мире. тогда синий гигант. Более того, изначально это был небольшой проект в компании Гордона Мура, поскольку 8800, выпущенный как iAPX 432, был намного лучше на бумаге с 32-битным ALU, интегрированным MMU и всем, что вам нужно для этого. многозадачность.
Что еще хуже, остались три мушкетера, создавшие первые три процессора Intel, одинокий Стивен Морс был выбран для разработки 8086 по двум причинам: он был инженером-электриком и инженером-программистом. Так что он прекрасно знал оба мира. Однако у 8086 была проблема, заключающаяся в том, что он запускался с совершенно новой ISA, и двоичный файл был несовместим. Если программы для CP / M подходили хорошо, то он был намного быстрее, чем Z80, но никто не хотел этого делать.
Морс покинул компанию вскоре после того, как 8086 стал самым большим провалом Intel на сегодняшний день, или нет.
IPC или инструкций на цикл
В компьютерной архитектуре , концепция инструкций за цикл относится к количеству инструкций, которые процессор выполняет одновременно, поэтому она в основном ограничена количеством исполнительных блоков в процессоре, но это не единственный ограничивающий фактор. поскольку могут быть инструкции, которые используют общие элементы процессора и вместе дают худшую производительность.
Но у этого термина есть сбивающий с толку вариант, основанный на том, чтобы взять время, которое два процессора требуется для выполнения одной и той же программы, а затем измерить разницу во времени, передать ее тактовым циклам и измерить разницу между ними. В результате получается среднее количество инструкций за цикл, но это не то, что в компьютерной архитектуре обычно называют IPC.
IPC, как мы сказали в начале, - это количество одновременных инструкций, которые процессор выполняет, и когда мы говорим «выполнение», мы имеем в виду то, что он решает в данный момент. Это не количество разрешенных инструкций, поскольку очень мало инструкций, если не сказать почти ни одной, решаются сегодня за один такт.
Как же понять, чем на самом деле занят процессор
Используя аппаратные счетчики производительности. В Linux они могут быть прочитаны с помощью perf и других аналогичных инструментов. Вот, например, замер производительности всей системы в течении 10 секунд:
Ключевая метрика здесь это "количество инструкций за такт" (insns per cycle: IPC), которое показывает, сколько инструкций в среднем выполнил процессор на каждый свой такт. Упрощённо: чем больше это число, тем лучше. В примере выше это число равно 0.78, что, на первый взгляд кажется не таким уж плохим результатом (78% времени выполнялась полезная работа?). Но нет, на этом процессоре максимально возможным значением IPC могло бы быть 4.0 (это связано со способом получения и выполнения инструкций современными процессорами). То есть наше значение IPC (равное 0.78) составляет всего 19.5% от максимально возможной скорости выполнения инструкций. А в процессорах Intel начиная со Skylake максимальное значение IPC уже равно 5.0.
В облаках
Когда вы работаете в виртуальном окружении, то можете и не иметь доступа к реальным счетчикам производительности (это зависит от используемого гипервизора и его настроек). Вот статья о том, как это работает в Amazon EC2.
Разделяемая память
Следующий тип межпроцессного взаимодействия — разделяемая память (shared memory). Схематично изобразим ее как некую именованную область в памяти, к которой обращаются одновременно два процесса:
Для выделения разделяемой памяти будем использовать POSIX функцию shm_open():
Функция возвращает файловый дескриптор, который связан с объектом памяти. Этот дескриптор в дальнейшем можно использовать другими функциями (к примеру, mmap() или mprotect()).
Целостность объекта памяти сохраняется, включая все данные связанные с ним, до тех пор пока объект не отсоединен/удален (shm_unlink()). Это означает, что любой процесс может получить доступ к нашему объекту памяти (если он знает его имя) до тех пор, пока явно в одном из процессов мы не вызовем shm_unlink().
- O_RDONLY — открыть только с правами на чтение
- O_RDWR — открыть с правами на чтение и запись
- O_CREAT — если объект уже существует, то от флага никакого эффекта. Иначе, объект создается и для него выставляются права доступа в соответствии с mode.
- O_EXCL — установка этого флага в сочетании с O_CREATE приведет к возврату функцией shm_open ошибки, если сегмент общей памяти уже существует.
После создания общего объекта памяти, мы задаем размер разделяемой памяти вызовом ftruncate(). На входе у функции файловый дескриптор нашего объекта и необходимый нам размер.
Пример
Следующий код демонстрирует создание, изменение и удаление разделяемой памяти. Так же показывается как после создания разделяемой памяти, программа выходит, но при следующем же запуске мы можем получить к ней доступ, пока не выполнен shm_unlink().
shm_open.c
После создания объекта памяти мы установили нужный нам размер shared memory вызовом ftruncate(). Затем мы получили доступ к разделяемой памяти при помощи mmap(). (Вообще говоря, даже с помощью самого вызова mmap() можно создать разделяемую память. Но отличие вызова shm_open() в том, что память будет оставаться выделенной до момента удаления или перезагрузки компьютера.)
Компилировать код на этот раз нужно с опцией -lrt:
Смотрим что получилось:
Аргумент «create» в нашей программе мы используем как для создания разделенной памяти, так и для изменения ее содержимого.
Зная имя объекта памяти, мы можем менять содержимое разделяемой памяти. Но стоит нам вызвать shm_unlink(), как память перестает быть нам доступна и shm_open() без параметра O_CREATE возвращает ошибку «No such file or directory».
Размер процессоров, почему он такой?
Возьмем, например, Intel Процессор Core i7-2700K архитектуры Sandy Bridge, выпущенный в 2011 году под 32-нанометровой литографией. Этот процессор Размер 37.5 x 37.5 мм . Если мы сравним его с Core i7-10700K с архитектурой Comet Lake, то мы увидим, что он изготовлен с толщиной 14 нанометров, но при этом его размер составляет 37.5 мм x 37.5 мм, что в точности соответствует процессору предыдущего восьмого поколения.
Наличие существенно более низкой литографии (14 против 32 нанометров) должно позволить производителю сделать процессор намного меньше, что обеспечит ряд преимуществ, таких как:
- Нижняя задержка между внутренними компонентами. Когда микросхема слишком велика, задержка скорости света / сопротивления может вызвать проблемы синхронизации, которые увеличивают задержку.
- Более дешевый для производства, используя в нем меньше сырья.
- Более высокий выход на пластину . В целом пластины имеют фиксированное количество дефектов. Чем больше процессоров вы можете получить на одну пластину, тем выгоднее ее производство, потому что вы получаете больше процессоров из одной пластины. Например, если вы получаете 10 процессоров из пластины, 5 выходят из строя - это катастрофа, но если вы получаете 500 процессоров из одной пластины, 5 выходят из строя дефектные, это тоже не так серьезно.
Итак, если создание процессоров меньшего размера дает так много преимуществ, почему они продолжают делать их одинакового размера?
Как вы хорошо знаете, каждый раз, когда они используют новую литографию процессора, которая всегда выходит из строя (то есть транзисторы меньше), плотность транзисторов значительно увеличивается. Следуя предыдущему примеру, Core i7-2700K имеет внутри 1.160 миллиона транзисторов, в то время как, когда мы говорим о Core i7-10700K, хотя точное количество неизвестно, это должно быть около 3.8 миллиарда транзисторов (более или менее как у Ryzen 3700X).
Это увеличение плотности транзисторов, содержащихся в кристалле процессора, позволяет значительно увеличить его IPC, общую производительность, количество физических ядер и его эффективность (производительность на потребляемый ватт). Другими словами, первая причина того, что переработчики не видят уменьшения своих размеров в новых поколениях, заключается в том, что сокращение литографии используется для повышения их производительности .
Вторая причина - проблемы с производством; Intel и AMD уже имеют «формы» определенного размера на своих заводах, и поддержание того же размера процессора позволяет им использовать большую часть того, что у них уже есть в производственном процессе, например, печатную плату или IHS без идти дальше. далеко, независимо от того, насколько сильно кристалл изменится внутри.
Вторая причина сохранения размера распространяется на две дополнительные причины: размер материнская плата гнездо , а размер теплоотводы . Если каждое новое поколение будет существенно менять размер процессора (речь идет о существенном изменении, а не о паре миллиметров, как это уже случалось несколько раз), производители материнских плат могут столкнуться с проблемами адаптации разъемов, не говоря уже о производителях радиаторов. , которые были бы вынуждены полностью обновить свою конструкцию и могли бы получить радиаторы, совместимые с несколькими платформами.
Последнее подводит нас к третьей причине, по которой процессоры сохраняют свой размер: температура . Чем меньше размер электронного компонента, тем меньше у нас будет рассеиваемой поверхности для отвода выделяемого им тепла. Если бы процессор был слишком маленьким, было бы трудно получить достаточно эффективный радиатор, чтобы рассеивать его тепло, и было бы довольно много проблем. Фактически, это то, над чем уже работали в течение многих лет, потому что размер процессоров может создавать большие неудобства, когда дело доходит до рассеивание выделяемого тепла .
Когда мы говорим, что один процессор имеет лучшую производительность, чем другой, что мы имеем в виду? Что ж, процессор выполняет программу с большей скоростью, чем другой, но какие параметры и условия используются для улучшения производительности процессора по сравнению с его преемником?
Маркетинговые отделы, когда речь идет о продаже такой сложной технологии, как ЦП или GPU / ГРАФИЧЕСКИЙ ПРОЦЕССОР придется упростить производительность, поскольку они становятся все более сложными, и именно одним из созданных ими заблуждений стал термин IPC как термин для измерения производительности процессора.
Именованный канал
Примечание: mode используется в сочетании с текущим значением umask следующим образом: (mode & ~umask). Результатом этой операции и будет новое значение umask для создаваемого нами файла. По этой причине мы используем 0777 (S_IRWXO | S_IRWXG | S_IRWXU), чтобы не затирать ни один бит текущей маски.
Как только файл создан, любой процесс может открыть этот файл для чтения или записи также, как открывает обычный файл. Однако, для корректного использования файла, необходимо открыть его одновременно двумя процессами/потоками, одним для получение данных (чтение файла), другим на передачу (запись в файл).
В случае успешного создания FIFO файла, mkfifo() возвращает 0 (нуль). В случае каких либо ошибок, функция возвращает -1 и выставляет код ошибки в переменную errno.
- EACCES — нет прав на запуск (execute) в одной из директорий в пути pathname
- EEXIST — файл pathname уже существует, даже если файл — символическая ссылка
- ENOENT — не существует какой-либо директории, упомянутой в pathname, либо является битой ссылкой
- ENOSPC — нет места для создания нового файла
- ENOTDIR — одна из директорий, упомянутых в pathname, на самом деле не является таковой
- EROFS — попытка создать FIFO файл на файловой системе «только-на-чтение»
Пример
mkfifo.c
Мы открываем файл только для чтения (O_RDONLY). И могли бы использовать O_NONBLOCK модификатор, предназначенный специально для FIFO файлов, чтобы не ждать когда с другой стороны файл откроют для записи. Но в приведенном коде такой способ неудобен.
Компилируем программу, затем запускаем ее:
В соседнем терминальном окне выполняем:
В результате мы увидим следующий вывод от программы:
Интерпретация данных и реагирование
Если у вас IPC > 1.0, то ваше приложение страдает не столько от ожидания данных, сколько от чрезмерного количества выполняемых инструкций. Ищите более эффективные алгоритмы, не делайте ненужной работы, кэшируйте результаты повторяемых операций. Применение инструментов построения и анализа Flame Graphs может быть отличным способом разобраться в ситуации. С аппаратной точки зрения вы можете использовать более быстрые процессоры и увеличить количество ядер.
Как вы видите, я провёл черту по значению IPC равному 1.0. Откуда я взял это число? Я рассчитал его для своей платформы, а вы, если не доверяете моей оценке, можете рассчитать его для своей. Для этого напишите два приложения: одно должно загружать процессор на 100% потоком выполнения инструкций (без активного обращения к большим блокам оперативной памяти), а второе должно наоборот активно манипулировать данным в ОЗУ, избегая тяжелых вычислений. Замерьте IPC для каждого из них и возьмите среднее. Это и будет примерная переломная точка для вашей архитектуры.
Что же такое загрузка процессора на самом деле?
Та метрика, которую мы называем «загрузкой процессора» на самом деле означает нечто вроде «время не-простоя»: то есть это то количество времени, которое процессор провёл во всех потоках кроме специального «Idle»-потока. Ядро вашей операционной системы (какой бы она ни была) измеряет это количество времени при переключениях контекста между потоками исполнения. Если произошло переключение потока выполнения команд на не-idle поток, который проработал 100 милисекунд, то ядро операционки считает это время, как время, потраченное CPU на выполнение реальной работы в данном потоке.
Эта метрика впервые появилась в таком виде одновременно с появлением операционных систем с разделением времени. Руководство программиста для компьютера в лунном модуле корабля «Апполон» (передовая на тот момент система с разделением времени) называла свой idle-поток специальным именем «DUMMY JOB» и инженеры сравнивали количество команд, выполняемых этим потоком с количеством команд, выполняемых рабочими потоками — это давало им понимание загрузки процессора.
Так что в этом подходе плохого?
Сегодня процессоры стали значительно быстрее, чем оперативная память, а ожидание данных стало занимать львиную долю того времени, которое мы привыкли называть «временем работы CPU». Когда вы видите высокий процент использования CPU в выводе команды top, то можете решить, что узким местом является процессор (железка на материнской плате под радиатором и кулером), хотя на самом деле это будет совсем другое устройство — банки оперативной памяти.
Ситуация даже ухудшается со временем. Долгое время производителям процессоров удавалось наращивать скорость их ядер быстрее, чем производители памяти увеличивали скорость доступа к ней и уменьшали задержки. Где-то в 2005-ом году на рынке появились процессоры с частотой 3 Гц и производители сконцентрировались на увеличении количества ядер, гипертрейдинге, много-сокетных конфигурациях — и всё это поставило ещё большие требования по скорости обмена данных! Производители процессоров попробовали как-то решить проблему увеличением размера процессорных кэшей, более быстрыми шинами и т.д. Это, конечно, немного помогло, но не переломило ситуацию кардинально. Мы уже ждём память большую часть времени «загрузки процессора» и ситуация лишь ухудшается.
Выводы
Загрузка процессора стала сегодня существенно недопонимаемой метрикой: она включает в себя время ожидания данных от ОЗУ, что может занимать даже больше времени, чем выполнение реальных команд. Вы можете определить реальную загрузку процессора с помощью дополнительных метрик, таких, как количество инструкций на такт (IPC). Значения меньшие, чем 1.0 говорят о том, что вы упираетесь в скорость обмена данными с памятью, а большие — свидетельствуют о большой загруженности процессора потоком инструкций. Инструменты замера производительности должны быть улучшены для отображения IPC (или чего-то аналогичного) непосредственно рядом с загрузкой процессора, что даст пользователю полное понимание ситуации. Имея все эти данные, разработчики могут предпринять некоторые меры по оптимизации своего кода именно в тех аспектах, где это принесёт наибольшую пользу.
С момента основания Гордоном Муром и Робертом Нойсом 18 июля 1968 года прошло более пяти десятилетий, в течение которых она стала ведущей компанией в производстве и разработке процессоров для персональных компьютеров. Вот почему мы решили совершить экскурсию по Intelистория через свои процессоры.
Intel известна своими процессорами с ISA x86, которые со временем претерпели несколько эволюций и стали универсальным языком для всех приложений, работающих на наших ПК, и хотя другие наборы регистров и инструкций появились сегодня, ISA, изобретенная Компания, которой сейчас руководит Пэт Гелсинджер в конце 1970-х, все еще сильна, как в первый день, если не сильнее.
Другие причины неверной трактовки термина «загрузка процессора»
Процессор может выполнять свою работу медленнее не только из-за потерь времени на ожидание данных из ОЗУ. Другими факторами могут быть:
- Перепады температуры процессора
- Вариирование частоты процессора технологией Turboboost
- Вариирование частоты процессора ядром ОС
- Проблема усреднённых расчётов: 80% средней загрузки на периоде измерений в минуту могут не быть катастрофой, но могут и прятать в себе скачки до 100%
- Спин-локи: процессор загружен выполнением инструкций и имеет высокий IPC, но на самом деле приложение стоит в спин-локах и не выполняет реальной работы
История Intel в 80-х: истоки ПК
В 1981 году в магазинах уже были персональные компьютеры, поэтому IBM не изобрела персональный компьютер, но постепенно они стали угрозой для голубого гиганта, и поэтому они решили создать малобюджетный проект под названием Project Chess, основанный на использовать существующее оборудование, и для этого они возьмут в качестве примера компьютеры S-100, производные от Altair 8800.
Семафор
Семафор — самый часто употребляемый метод для синхронизации потоков и для контролирования одновременного доступа множеством потоков/процессов к общей памяти (к примеру, глобальной переменной). Взаимодействие между процессами в случае с семафорами заключается в том, что процессы работают с одним и тем же набором данных и корректируют свое поведение в зависимости от этих данных.
- семафор со счетчиком (counting semaphore), определяющий лимит ресурсов для процессов, получающих доступ к ним
- бинарный семафор (binary semaphore), имеющий два состояния «0» или «1» (чаще: «занят» или «не занят»)
Семафор со счетчиком
Смысл семафора со счетчиком в том, чтобы дать доступ к какому-то ресурсу только определенному количеству процессов. Остальные будут ждать в очереди, когда ресурс освободится.
Итак, для реализации семафоров будем использовать POSIX функцию sem_open():
В функцию для создания семафора мы передаем имя семафора, построенное по определенным правилам и управляющие флаги. Таким образом у нас получится именованный семафор.
Имя семафора строится следующим образом: в начале идет символ "/" (косая черта), а следом латинские символы. Символ «косая черта» при этом больше не должен применяться. Длина имени семафора может быть вплоть до 251 знака.
Если нам необходимо создать семафор, то передается управляющий флаг O_CREATE. Чтобы начать использовать уже существующий семафор, то oflag равняется нулю. Если вместе с флагом O_CREATE передать флаг O_EXCL, то функция sem_open() вернет ошибку, в случае если семафор с указанным именем уже существует.
Параметр mode задает права доступа таким же образом, как это объяснено в предыдущих главах. А переменной value инициализируется начальное значение семафора. Оба параметра mode и value игнорируются в случае, когда семафор с указанным именем уже существует, а sem_open() вызван вместе с флагом O_CREATE.
Для быстрого открытия существующего семафора используем конструкцию:
, где указываются только имя семафора и управляющий флаг.
Пример семафора со счетчиком
Рассмотрим пример использования семафора для синхронизации процессов. В нашем примере один процесс увеличивает значение семафора и ждет, когда второй сбросит его, чтобы продолжить дальнейшее выполнение.
sem_open.c
В одной консоли запускаем:
В соседней консоли запускаем:
Бинарный семафор
Вместо бинарного семафора, для которого так же используется функция sem_open, я рассмотрю гораздо чаще употребляемый семафор, называемый «мьютекс» (mutex).
Мьютекс по существу является тем же самым, чем является бинарный семафор (т.е. семафор с двумя состояниями: «занят» и «не занят»). Но термин «mutex» чаще используется чтобы описать схему, которая предохраняет два процесса от одновременного использования общих данных/переменных. В то время как термин «бинарный семафор» чаще употребляется для описания конструкции, которая ограничивает доступ к одному ресурсу. То есть бинарный семафор используют там, где один процесс «занимает» семафор, а другой его «освобождает». В то время как мьютекс освобождается тем же процессом/потоком, который занял его.
Без мьютекса не обойтись в написании, к примеру базы данных, к которой доступ могут иметь множество клиентов.
Для использования мьютекса необходимо вызвать функцию pthread_mutex_init():
Функция инициализирует мьютекс (перемнную mutex) аттрибутом mutexattr. Если mutexattr равен NULL, то мьютекс инициализируется значением по умолчанию. В случае успешного выполнения функции (код возрата 0), мьютекс считается инициализированным и «свободным».
- EAGAIN — недостаточно необходимых ресурсов (кроме памяти) для инициализации мьютекса
- ENOMEM — недостаточно памяти
- EPERM — нет прав для выполнения операции
- EBUSY — попытка инициализировать мьютекс, который уже был инициализирован, но не унечтожен
- EINVAL — значение mutexattr не валидно
Функция pthread_mutex_lock(), если mutex еще не занят, то занимает его, становится его обладателем и сразу же выходит. Если мьютекс занят, то блокирует дальнейшее выполнение процесса и ждет освобождения мьютекса.
Функция pthread_mutex_trylock() идентична по поведению функции pthread_mutex_lock(), с одним исключением — она не блокирует процесс, если mutex занят, а возвращает EBUSY код.
Фунция pthread_mutex_unlock() освобождает занятый мьютекс.
- EINVAL — mutex неправильно инициализирован
- EDEADLK — мьютекс уже занят текущим процессом
- EBUSY — мьютекс уже занят
- EINVAL — мьютекс неправильно инициализирован
- EINVAL — мьютекс неправильно инициализирован
- EPERM — вызывающий процесс не является обладателем мьютекса
Пример mutex
mutex.c
Данный пример демонстрирует совместный доступ двух потоков к общей переменной. Один поток (первый поток) в автоматическом режиме постоянно увеличивает переменную counter на единицу, при этом занимая эту переменную на целую секунду. Этот первый поток дает второму доступ к переменной count только на 10 миллисекунд, затем снова занимает ее на секунду. Во втором потоке предлагается ввести новое значение для переменной с терминала.
Если бы мы не использовали технологию «мьютекс», то какое значение было бы в глобальной переменной, при одновременном доступе двух потоков, нам не известно. Так же во время запуска становится очевидна разница между pthread_mutex_lock() и pthread_mutex_trylock().
Компилировать код нужно с дополнительным параметром -lpthread:
Запускаем и меняем значение переменной просто вводя новое значение в терминальном окне:
Intel 8008 и терминал Datapoint
Эксперимент с i4004 помог Intel утвердить себя для остальной отрасли, но они все еще не были гигантами, которыми станут годы спустя. Эра ПК еще не наступила, и несколько пользователей компьютеров продолжали использовать терминалы с разделением времени, подключенные к мини-компьютеру, который, как это ни парадоксально, мог быть размером с холодильник.
Одной из этих компаний была Datapoint Corporation, и у ее модели терминала 2200 была особенность, заключающаяся в том, что ее схема не только служила удаленным терминалом, но и имела полный компьютер, поскольку она могла выполнять программы благодаря наличию интегральной схемы. процессора внутри. И снова трем архитекторам, отвечающим за i4004, была поставлена задача сократить всю сложную схему 8008, которую вы видите над этими изображениями на i8008.
80486, небольшое, но важное обновление
Четвертое поколение процессоров x86 в истории Intel принесло нам две важные новинки, первая из которых - кеширование данных и инструкций первого уровня. Второй - интеграция модуля с плавающей запятой, который больше не был отдельным сопроцессором и стал неотъемлемой частью ЦП. Его конвейер или удлиненная сегментация на 5 циклов позволили ему достичь 100 МГц с моделью DX4.
Также это был гвоздь в гроб соперника. Motorola, которые в итоге объединились с IBM и Apple для создания PowerPC. Причина? Intel не была удовлетворена слегка улучшенной версией 386 и повторила ход 286 с невероятным увеличением IPC, что подтвердило большую мощность ПК по сравнению с его конкурентами, но особенно дало Intel окончательную победу над Motorola.
Любопытно, что его главным архитектором был Пэт Гелсингер, нынешний генеральный директор Intel.
Intel Pentium, первый в истории мультимедийный процессор
Intel Pentium был первым суперскалярным процессором под ISA x86, который принес с собой возможность выполнять 2 инструкции одновременно и одновременно. Благодаря этому CPI по сравнению с 80486 улучшился на 40% в процессе.
Спустя годы Intel выпустила Pentium MMX, который внес изменения в блок с плавающей запятой, поскольку в нем реализован SIMD поверх регистра для ускорения зарождающихся мультимедийных программ того времени. Но коммерческая жизнь Pentium MMX была недолгой, поскольку в 1997 году, когда они были выпущены, они совпали на рынке с Pentium II, всего через год после улучшенной версии процессора Intel x86 пятого поколения.
На архитектурном уровне изменения были незначительными по сравнению с 80486, но дизайн снова вернулся в руки тех, кто создал оригинальный 80386, завершивший его в 1992 году. В конце концов, вне суперскалярного дизайна он был очень консервативным. , компания перестала иметь конкуренцию, однако это не означало, что они бездействовали.
CPI или количество циклов на инструкцию, истинная мера производительности
Реальность в процессорах такова, что не все инструкции занимают одинаковое количество циклов для разрешения , То есть Цикл каждой инструкции зависит от ее сложности и количества шагов, которые она должна пройти.
Один из способов, которым архитекторы могут улучшить производительность процессоров, - это уменьшить количество шагов, необходимых ЦП для выполнения определенных конкретных инструкций, таким образом, чтобы в зависимости от производительности при выполнении потока или программы вы в конечном итоге увеличивали скорость выполнение программы за счет ускорения выполнения этих инструкций.
Это явление происходит как в процессорах, так и в графических процессорах, и это наиболее распространенный способ повышения производительности процессора, поскольку набор регистров и инструкций - это то, что делает инструкция, адресная память, которую она использует, и записи, но не указывают как.
Например. В этой сравнительной таблице вы можете увидеть эволюцию архитектуры графических процессоров GCN от AMD к RDNA от той же компании, поскольку вы можете видеть, что одна и та же программа шейдера занимает меньше циклов в архитектуре RDNA, поскольку значительная часть ее инструкций имеет более низкий CPI.
Pentium 4, конец эпохи
Для Pentium 4 Intel создала новую архитектуру под названием Netburst, которая следовала модной тенденции дня и добавляла большое количество ступеней для достижения высокой тактовой частоты. Именно с этим процессором Intel достигла потолка скорости, и было обнаружено, что гонка, основанная на этой метрике, не имеет будущего из-за высокого потребления процессоров и температуры, которую они генерируют.
Благодаря опыту с Pentium 4 показатель «мощность на ватт» стал иметь значение и начал разрабатывать процессоры, уже основанные на концепции многоядерности. В частности, причина заключалась в том, что их было невозможно установить на ноутбуках, и пришлось продлить срок службы P6, чтобы иметь возможность запускать процессоры для того типа компьютеров, которые создавались в то время.
Сплетни говорят, что именно Apple, одержимая своими промышленными разработками, дала Intel понять, сможет ли она создать ядро с достаточной производительностью на ватт и большей мощностью, чем PowerPC, только тогда она совершит прыжок на x86. Так оно и было, но вместе с этим они отказались от названия Pentium и приняли другое.
Программное обеспечение - это то, что командует в конце дня
Процессор выполняет программу, и важно то, что наиболее часто используемые программы на рынке в конечном итоге получают все большую и большую производительность, поэтому не только инструкции процессоров оптимизированы для повышения производительности в тестах, но также к тому, что в наиболее часто используемых программах и функциях есть разница.
В некоторых случаях создаются новые варианты наборов команд и исполнительных блоков, как это произошло с появлением модулей SIMD в конце 1990-х годов для мультимедийного контента или это происходит с инструкциями по ускорению алгоритмов искусственного интеллекта.
Повышение производительности не всегда должно сопровождаться сокращением количества циклов на инструкцию, оно может проявляться в виде новых исполнительных модулей и даже поддержки сопроцессоров.
Читайте также: