Основы информационных технологий для неспециалистов: что происходит внутри машин — страница 7 из 26

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

Артур У. Бёркс, Герман Х. Голдстайн, Джон фон Нейман.

Предварительное рассмотрение логического устройства электронного вычислительного прибора, 1946

В главе 1 я упомянул, что процессор, или ЦПУ, – «мозг» компьютера, хотя и с оговоркой, что это определение употребляется не в прямом значении. Сейчас пришло время более подробно рассмотреть процессор, поскольку это самый важный элемент вычислительной машины и его свойства имеют наибольшее значение для оставшейся части книги.

Как работает процессор? Что за процессы в нем идут и как? При первом близком рассмотрении ЦПУ мы обнаруживаем, что оно способно выполнять набор базовых операций. Процессор может, подобно калькулятору, производить арифметические операции: сложение, вычитание, умножение и деление чисел. Он извлекает данные из памяти, обрабатывает их и сохраняет результаты туда же, как действуют многие счетные устройства. Процессор управляет остальными частями компьютера – отправляет по шине сигналы для контроля и координации ввода и вывода от всего, что электрически соединено с ним, включая мышь, клавиатуру, экран и многое другое.

Самое главное – ЦПУ принимает решения, пусть и простые. Процессор способен сравнивать как числа (больше или меньше), так и данные других типов (совпадает ли эта часть информации с другой), и на основании полученных результатов решать, что делать дальше. Это самое важное его свойство, поскольку, хотя процессору доступно немногим больше, чем калькулятору, он умеет работать без вмешательства человека. Как сказали Беркс, Голдстайн и фон Нейман, «предполагается, что машина будет полностью автоматической по своей природе, то есть независимой от человека-оператора после начала вычислений».

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

3.1. Компьютер-игрушка

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

Я привык называть такую выдуманную машину «компьютером-игрушкой», или просто Игрушкой, поскольку она не настоящая, но обладает свойствами реальной. По возможностям она примерно соответствует уровню мини-ЭВМ конца 1960-х годов и напоминает устройство, описанное в первой статье Беркса, Голдстайна и фон Неймана. В компьютере-игрушке есть память, отведенная под инструкции и данные, а также одна дополнительная область хранения – накопитель, емкости которого достаточно для удержания одного числа. Накопитель – это аналог экрана на калькуляторе: он показывает число, только что введенное пользователем, или последние результаты вычислений. Компьютер-игрушка содержит около десяти инструкций для выполнения основных операций, описанных выше. На рис. 3.1 приведены первые шесть из данного набора.


Рис. 3.1. Типовые инструкции в машине-игрушке


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


Fetch: извлечь следующую инструкцию из памяти

Decode: определить, что выполнять по этой инструкции (декодировать)

Execute: выполнить инструкцию вернуться к Fetch

3.1.1. Первая программа компьютера-игрушки

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


GET

PRINT

STOP


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

И, к счастью, рабочие игрушечные компьютеры существуют. На рис. 3.2 показан один из них в действии.

Это симулятор, написанный на JavaScript, поэтому его можно запустить на любом браузере, как мы увидим в главе 7.


Рис. 3.2. Симулятор компьютера-игрушки с готовой к запуску программой


Когда вы нажимаете ПУСК, выполняется инструкция GET и появляется диалоговое окошко (см. рис. 3.3). Число 123 введено пользователем.


Рис. 3.3. Диалоговое окно ввода симулятора Игрушки


После того как пользователь напечатал число и нажал ОК, симулятор запускается и отображает результаты, как показано на рис. 3.4. Как и было обещано, программа запрашивает введенное число, отображает его и останавливается.


Рис. 3.4. Симулятор Игрушки после выполнения короткой программы

3.1.2. Вторая программа компьютера-игрушки

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

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

Единственная сложность заключается в том, что нам нужно выделить место в памяти для хранения данных – первого числа, которое будет считываться. Мы не можем оставить первое число в накопителе, так как вторая инструкция (GET) перезапишет его. Поскольку число – это данные, а не инструкция, мы должны хранить его в определенном месте в памяти, где оно не будет истолковано как инструкция. Но, если мы поместим его в конец программы, после всех инструкций, процессор точно не попытается интерпретировать значение данных как инструкцию, потому что остановится (STOP) и не доберется до него.


Рис. 3.5. Программа компьютера-игрушки для сложения двух чисел и вывода суммы


Нам также нужен способ ссылаться на место хранения, когда этого потребуют инструкции программы. Мы можем заметить одну возможность для хранения данных – седьмую ячейку памяти (после шестой инструкции) – и написать «STORE 7». В конечном итоге наша программа будет сохранена в такой форме. Но местоположение (адрес) может меняться при изменении программы. Решение в том, чтобы присвоить ему имя: как мы увидим в главе 5, любая программа способна выполнять такую «конторскую» задачу, то есть отслеживать, где в памяти находятся данные, заменяя имя правильным положением числа. FirstNum указывает, что это «первое число». Имя задается произвольно, хотя рекомендуется использовать то, которое отражает предназначение или смысл связанных с ним данных или инструкций. После имени мы поставили двоеточие, чтобы определить FirstNum как метку. По соглашениям о именовании, инструкции в программе набирают с отступом, а имена, привязанные к инструкциям или ячейкам памяти, – без. В симуляторе Игрушки учитываются все эти детали.

3.1.3. Инструкции ветвления

Как нам расширить программу, показанную на рис. 3.5, чтобы она суммировала три числа? Можно просто добавить еще одну последовательность инструкций – STORE, GET и ADD (для них есть два свободных места), но такое «расширение», конечно, не позволит суммировать тысячу чисел. Программа вообще не даст результата, если мы не будем знать заранее, сколько чисел нужно сложить.

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

С помощью инструкции GOTO мы можем заставить процессор вернуться к предшествующей части программы и повторить выполнение команд. Вот несложный пример: программа, которая выводит каждое число по мере его ввода. По сути, так работает любая программа, которая копирует или отображает ввод данных для нее, и здесь показывается, что делает инструкция GOTO. Первая инструкция программы, изображенная на рис. 3.6, называется Тор («Верх»). Имя задано произвольно и указывает на ее роль. Наконец, последняя инструкция заставляет процессор вернуться к первой.


Рис. 3.6. Программа копирования данных, которая выполняется вечно


Это отчасти нам помогает: мы можем неоднократно использовать инструкции. Но перед нами все еще стоит критическая проблема: невозможно остановить бесконечное повторение последовательности инструкций, или цикла. Чтобы прервать цикл, нам нужна инструкция другого вида, которая позволит не бездумно продолжать, а проверить некое условие и решить, что делать дальше. Инструкция такого рода называется условным ветвлением или условным переходом. Одна из возможностей, которая есть в любом компьютере, – инструкция, которая проверяет, равно ли какое-либо значение нулю, и, если это так, переходит к определенной команде. К счастью, в Игрушке есть инструкция под названием IFZERO, которая переходит к заданной команде, если значение в накопителе равно нулю. В противном случае выполняется следующая инструкция в последовательности.

Давайте задействуем инструкцию IFZERO, чтобы написать программу (см. рис. 3.7), которая считывает и печатает входные значения до тех пор, пока в них не появится значение, равное 0. Она будет извлекать и отображать данные, пока пользователь не устанет и не введет ноль, после чего программа перейдет к инструкции STOP, помеченной как Bot, то есть bottom («низ»), и остановится. (Заманчиво написать IFZERO STOP, но так не сработает: за IFZERO должна следовать ячейка, а не инструкция.)


Рис. 3.7. Программа копирования данных, которая останавливается при вводе О


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

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

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

Я говорю «в принципе», потому что на практике мы обязаны учитывать скорость процессора, объем памяти, ограниченность величины чисел в компьютере и тому подобное. Так или иначе, иногда мы будем возвращаться к идее эквивалентности всех ЭВМ, поскольку это фундаментальная концепция.

В качестве еще одного примера работы IFZERO и GOTO на рис. 3.8 приведена программа, которая суммирует несколько чисел и останавливается, когда введен 0. Какое-либо специальное значение сплошь и рядом применяют для того, чтобы завершить последовательность ввода. В данном случае ноль вполне подходит в качестве метки окончания, потому что мы складываем числа, а добавлять данные с нулевым значением не обязательно.

Симуляторы компьютера-игрушки интерпретируют «инструкции» вроде последней строчки в этой программе так: «определить имя для ячейки памяти и поместить определенное цифровое значение в эту ячейку перед тем, как программа будет запущена». Это не настоящая инструкция, а скорее «псевдоинструкция», которая интерпретируется симулятором при обработке текста программы перед ее запуском.

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


Рис. 3.8. Программа Игрушки для сложения последовательности чисел


Как бы вы проверили эту программу, чтобы убедиться, что она работает? На первый взгляд все выглядит нормально, и она дает правильные ответы на простые тестовые задачи. Упустить из виду проблему легко, поэтому нужна систематическая проверка. Ключевое слово – «систематическая»: просто вводить в программу случайные данные будет неэффективно.

Какой самый простой тестовый пример? Если мы не вводим никаких чисел, кроме нуля, который завершает ввод, то сумма будет равняться нулю, так что в качестве первой проверки годится. Потом нужно попробовать ввести одно число: сумма должна совпасть с ним. Затем надо ввести два числа, сумма которых уже вам известна (например, 1 и 2, где результат должен равняться 3). После нескольких таких тестов вы можете с достаточной степенью уверенности утверждать, что программа работает. Если вы скрупулезны, то проверите код еще до того, как он попадет на компьютер, – тщательно пройдетесь по всей последовательности инструкций. Хороший программист поступает так с каждой написанной им программой.

3.1.4. Представление в памяти

До сих пор я уклонялся от ответа на вопрос о том, как инструкции и данные представлены в памяти. Как это работает?

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

Предположим также, что любое значение данных помещается в одной ячейке. Это все упрощения, однако они не слишком искажают картину того, что происходит в реальных компьютерах. Наконец, примем, что числовые значения инструкций следующие: GET = 1, PRINT =2, STORE = 3, LOAD = 4, ADD = 5, STOP = 6, GOTO = 7, IFZERO = 8. В таком порядке они появлялись на предыдущих страницах.

Программа на рис. 3.8 суммирует последовательность чисел. Когда программа начинает работать, содержимое памяти будет таким, как показано на рис. 3.9. Здесь также изображены фактические ячейки памяти, три привязанные к ним метки, инструкции и адреса, соответствующие содержимому памяти.


Рис. 3.9. Как программа сложения чисел выглядит в памяти


Симулятор Игрушки написан на JavaScript – языке программирования (ЯП), о котором пойдет речь в главе 7, – но мы могли выбрать любой другой ЯП. Данный симулятор легко расширить. Например, вы без труда добавите инструкцию умножения или условную ветвь другого типа, даже если никогда раньше не видели компьютерную программу. Это хороший способ проверить, как вы усвоили материал. Код можно найти на веб-сайте этой книги.

3.2. Настоящие процессоры

Мы рассмотрели упрощенную версию процессора, но он не сильно отличается от тех, что стояли в ранних вычислительных устройствах или сейчас применяются в маленьких. Современные ЦПУ гораздо сложнее в деталях, и все сосредоточено вокруг производительности.

Любой процессор снова и снова производит цикл извлечения, декодирования, выполнения. Он извлекает из памяти очередную инструкцию, которая обычно хранится в следующей ячейке памяти, но ее местоположение также могли задать GOTO или IFZERO. Процессор декодирует инструкцию (то есть выясняет, что она делает) и совершает все необходимые приготовления для ее запуска. Затем он выполняет инструкцию: вытаскивает информацию из памяти, производит арифметические или логические действия и сохраняет результат в подходящей комбинации битов. Потом ЦПУ возвращается к шагу извлечения. Цикл «извлечение – декодирование – выполнение» в настоящем процессоре задействует хитроумные методы, которые ускоряют всю процедуру, но фундаментально там нет разницы с циклом для сложения чисел, показанным выше.

Настоящие компьютеры выполняют больше инструкций, чем наша Игрушка, хотя они относятся к тем же основным видам. Они знают больше способов того, как перемещать данные, выполнять арифметические действия (в том числе для различных размеров и типов чисел), сравнивать и разветвлять, а также управлять остальными частями машины. Типичное ЦПУ имеет от пары десятков до пары сотен различных инструкций, которые вместе с данными занимают несколько ячеек памяти, обычно от 2 до 8 байт на каждую. В реальном процессоре есть много накопителей, чаще всего от 16 до 32. Поэтому он может хранить более одного промежуточного результата в своей, скажем так, чрезвычайно быстродействующей памяти.

Настоящие программы колоссальны по сравнению с нашими примерами для Игрушки, и часто в них содержатся миллионы инструкций. Мы еще вернемся к тому, как пишутся программы, когда будем говорить о ПО в последующих главах.

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

Одна из проблем, рассматриваемых архитектурой ЭВМ, – размер набора инструкций, то есть совокупности команд, которые выполняет процессор. Что лучше: множество инструкций для широкого спектра различных вычислений или небольшое количество команд – таких, которые проще создавать и которые будут выполняться быстрее? Разработка архитектуры предполагает поиск многогранного баланса функциональности, скорости, сложности, энергопотребления и программируемости. Снова процитируем фон Неймана: «В общем внутренняя структура арифметического устройства определяется компромиссом между стремлением к быстроте работы <…> и к простоте или дешевизне машины».

Как процессор соединяется с оперативной памятью и остальными частями компьютера? Начнем с того, что ЦПУ действуют очень быстро, выполняя одну инструкцию менее чем за наносекунду. (Напомним, что «нано» – это одна миллиардная, или 10“9.) Для сравнения, память чрезвычайно медленная: извлечение данных или инструкций из нее занимает от 10 до 20 наносекунд. Конечно, это молниеносно в абсолютном выражении, но неторопливо по меркам функционирования процессора. Он мог бы выполнить десятки инструкций за то время, что ему приходится ждать поступления данных.

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

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

Еще один вариант – одновременная работа нескольких процессоров. Сегодня это норма для ноутбуков и мобильных телефонов. В процессоре Intel моего компьютера 2015 года выпуска, на котором я сейчас печатаю, есть два ядра на одном чипе интегральной схемы. Сейчас наблюдается сильная тенденция в сторону увеличения количества ядер ЦПУ на чипе, а также на установку более одного чипа в устройстве. Поскольку размеры элементов интегральной схемы уменьшаются, на микросхему удается поместить больше транзисторов, и они, как правило, используются для того, чтобы добавить ядер и увеличить кэш-память. Процессоры сами по себе не ускоряются, но ядер становится больше, и фактическая быстрота вычислений продолжает расти.

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

В случае сотовых телефонов, планшетов и других портативных устройств требуется идти на еще большие компромиссы, поскольку размер, вес и мощность уменьшаются еще заметнее. Здесь уже недостаточно только «подкрутить» конструкцию. Если Intel и ее основной конкурент AMD – главные поставщики ЦПУ для компьютеров и ноутбуков, то в большинстве сотовых телефонов и планшетов используется процессор под названием ARM, специально спроектированный для работы с низким энергопотреблением. Структура таких ЦПУ запатентована английской компанией Arm Holdings.

Сравнение скоростей процессоров – дело сложное и, по сути, бессмысленное. Даже базовые операции вроде арифметических действий могут выполняться настолько разными способами, что трудно провести прямое сопоставление. Например, одному процессору понадобится выполнить три инструкции, чтобы сложить два числа и сохранить результат в ходе третьей, как делала Игрушка. Другому для такого вычисления хватит двух команд, а третьему – всего одной. Некоторые ЦПУ способны параллельно обрабатывать несколько инструкций или совмещать их, чтобы они выполнялись поэтапно. Процессоры умеют жертвовать скоростью работы ради снижения энергопотребления и даже динамически регулировать ее в зависимости от того, поступает энергия из батареи или из сети. В отдельные ЦПУ встроены как быстрые, так и медленные ядра для выполнения разных задач. Поэтому вам следует с осторожностью заявлять о том, что один процессор «шустрее» другого, – вероятно, ваша оценка субъективна.

3.3. Кэширование

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

Артур У. Бёркс, Герман Х. Голдстайн, Джон фон Нейман.

Предварительное рассмотрение логического устройства электронного вычислительного прибора, 1946

Здесь стоит ненадолго отойти от темы и рассмотреть кэширование – идею, широко применимую за пределами вычислений как таковых. В процессоре кэш — это маленькая быстрая память, где хранится недавно обработанная информация. Тем самым удается избежать постоянных обращений к оперативной памяти, которая больше по объему, но действует значительно медленнее. Как правило, процессор неоднократно обращается к группам данных и инструкций за короткий отрезок времени. Например, пять инструкций в цикле программы, приведенной на рис. 3.9, будут выполняться по одному разу для каждого введенного числа. Если хранить их в кэше, то извлекать их из ОЗУ для каждого цикла уже не понадобится, и тогда программа начнет выполняться быстрее, поскольку для обработки инструкций не придется ждать их поступления из памяти. Аналогично, если держать Sum (переменную из программы для Игрушки) в кэше данных, доступ тоже ускорится, однако узкое место в этой программе – получение данных.

Типичный процессор имеет два или три кэша, которые последовательно увеличиваются по емкости и уменьшаются в скорости. Их часто называют уровнями LI, L2 и L3. Самый большой из них способен вмещать несколько мегабайт данных. (На моем ноутбуке 256 Кб кэша L2 для каждого ядра и 4 Мб в одном кэше L3.) Кэширование приносит пользу, так как велика вероятность, что недавно использованная информация вскоре понадобится вновь, а наличие ее в кэше уменьшит время ожидания отклика памяти. Обычно в процессе кэширования одновременно загружаются все блоки данных – например, блок последовательных ячеек памяти, когда запрашивается один байт. Это связано с тем, что «примыкающая» информация, вероятно, также скоро будет использоваться – и значит, почти наверняка уже окажется в кэше, когда в ней возникнет надобность. Тогда ссылкам на соседние данные не придется ждать.

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

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

Иногда преимущества кэширования вполне очевидны. Запустите какую-нибудь объемную программу вроде Word или Firefox и замерьте время, которое понадобится ей для полной загрузки с диска. Потом закройте ее и немедленно перезапустите. Обычно во второй раз программа стартует намного быстрее, потому что ее инструкции все еще находятся в ОЗУ, которая играет роль кэша для жесткого диска. В дальнейшем, пока вы будете работать в других приложениях, память начнет заполняться их инструкциями и данными. Они «вытеснят» предыдущую программу из кэша.

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

3.4. Другие виды вычислительных устройств

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

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

В суперкомпьютерах, как правило, устанавливают большое количество ЦПУ и громадный объем памяти. Сами их процессоры порой имеют инструкции, которые обрабатывают определенные виды данных намного быстрее, чем их более привычные собратья. Правда, современные суперкомпьютеры основаны на кластерах быстрых, но в целом обычных процессоров, а не на специализированном оборудовании. Каждые полгода на вебсайте top500.org публикуется новый список 500 самых быстрых компьютеров в мире. Поразительно, как быстро повышается максимальная скорость: машины, которые находились в первой десятке несколько лет назад, сегодня вообще отсутствуют в списке. Лучшая машина на ноябрь 2020 года, построенная компанией Fujitsu в Японии, имеет 7,6 миллиона ядер и может выполнять максимум 537 × 1015 арифметических операций в секунду[20]. Скорость суперкомпьютеров измеряется количеством операций с плавающей запятой, или флопами, т. е. количеством арифметических операций с числами с дробной долей в секунду. Лидер списка top500.org показывает результат в 537 петафлопов, а компьютер на 500-м месте – 2,4 петафлопа.

Графический процессор (GPU) – это специализированный процессор, который выполняет определенные графические вычисления намного быстрее, чем ЦПУ общего назначения. Такие устройства изначально разрабатывались для высокоскоростных расчетов графики, необходимой для игр, но они также применяются для обработки речи и сигналов в телефонах. GPU помогают ускорить работу обычных ЦПУ при определенных видах рабочих нагрузок. Графические процессоры могут параллельно выполнять большое количество простых арифметических вычислений, поэтому если какую-либо часть задачи можно передать для выполнения в GPU, то операция в целом будет выполняться быстрее. Данные устройства особенно полезны для машинного обучения (глава 12), при котором одни и те же вычисления производятся независимо в разных частях большого набора данных.

Распределенные вычисления выполняются на компьютерах, которые более независимы: они, например, не используют общую память, а иногда физически рассредоточены, вплоть до того, что находятся в разных частях света. Из-за такого подхода повышается вероятность того, что связь станет узким местом, но он позволяет людям и компьютерам взаимодействовать на больших расстояниях. Крупномасштабные веб-сервисы – поисковые системы, интернет-магазины, социальные сети и облачная обработка данных в целом – это распределенные вычислительные системы, в которых тысячи ЭВМ объединяются, чтобы быстрее получить результат для большого количества пользователей.

Все эти типы компьютеров базируются на одних и тех же фундаментальных принципах. Они основаны на процессоре общего назначения, который можно запрограммировать для выполнения бесконечно разнообразных задач. Каждый ЦПУ имеет ограниченный набор простых инструкций для выполнения арифметических действий, сравнения значений данных и выбора следующей инструкции на основании результатов предыдущих вычислений. Определяющая архитектура ЭВМ не сильно изменилась с конца 1940-х годов, но их физическая конструкция непрерывно эволюционировала поразительными темпами.

Возможно, это прозвучит неожиданно, но все упомянутые компьютеры обладают одинаковыми логическими возможностями и способны «обсчитывать» буквально одно и то же, если оставить в стороне такие практические факторы, как требования к скорости и памяти. Данную теорию независимо друг от друга доказали в 1930-х годах несколько ученых, включая английского математика Алана Тьюринга. Именно его подход легче всего понять неспециалисту. Тьюринг описал элементарное счетное устройство, гораздо проще нашей Игрушки, и показал, что оно может вычислить всё, что поддается вычислениям в самом общем смысле. Сегодня такой компьютер называется машиной Тьюринга29. Затем Алан показал, как создать машину Тьюринга, которая способна действовать как симулятор других подобных машин. Теперь такое устройство обозначается как универсальная машина Тьюринга (УМТ). Составить программу, которая будет имитировать УМТ, несложно. Также возможно (но уже не просто) написать программу для УМТ, которая будет имитировать настоящий компьютер. Следовательно, все компьютеры равны в том, что они умеют вычислять, но различаются в скорости обработки.

Во время Второй мировой войны Алан перешел от теории к практике: он сыграл главную роль в разработке специализированного компьютера для дешифрирования немецких военных сообщений30, о чем мы еще кратко поговорим в главе 13. Работы Тьюринга на благо фронта показаны – со значительными художественными отступлениями – в нескольких фильмах, в частности «Взлом кода» (Breaking The Code, телеканал ВВС) 1996 года и «Игра в имитацию» 2014 года.

В 1950 году Тьюринг опубликовал статью под названием «Вычислительные машины и разум»31, в которой предлагался тест (сегодня называемый тестом Тьюринга), который подошел бы для оценки того, демонстрирует ли компьютер человеческий интеллект. Представьте себе, как вычислительная машина и человек по отдельности общаются через клавиатуру и экран с человеком-опросчиком. Ведя такую беседу, может ли опрашивающий установить, кто из его собеседников – человек, а кто – компьютер? Тьюринг полагал, что, если их не удастся надежно различить, значит, ЭВМ демонстрирует разумное поведение. Как мы увидим в главе 12, компьютеры сейчас общаются на уровне человека или даже выше по некоторым темам – хотя, конечно, к общему умственному развитию это никак не относится.


Рис. 3.10. CAPTCHA32


Имя Тьюринга упоминается в несколько вымученной аббревиатуре CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart), она же «капча», которая расшифровывается как «Полностью автоматизированный публичный тест Тьюринга, позволяющий отличать компьютеры от людей»[21]. CAPTCHA представляет собой искаженные наборы букв вроде тех, что приведены на рис. 3.10. Она используется для попыток проверить, человек ли посетитель веб-сайта или программа. «Капча» – пример обратного теста Тьюринга, поскольку она пробует отличить человека от компьютера, полагаясь на тот факт, что люди, как правило, лучше машин распознают графические структуры. Конечно, CAPTCHA неприменима для тех, кто страдает нарушениями зрения.

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

В 1952 году Тьюринга привлекли к ответственности за гомосексуальную активность, которая в то время в Англии преследовалась по закону. Он умер в 1954 году, по-видимому, покончив с собой34.

3.5. Краткие выводы

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

Современные компьютеры почти всегда оснащены несколькими ядрами на одном чипе. Также в них встречается несколько микросхем процессоров, а для более эффективного доступа к памяти на интегральной схеме размещают объемный кэш. Кэширование – это фундаментальный принцип в вычислительной технике, который мы встречаем на всех уровнях от ЦПУ до интернета. Чаще всего оно основывается на локальности данных (во времени или пространстве), что обеспечивает более быстрый доступ к ним.

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

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

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

Алан Тьюринг. Вычислительные машины и разум[22].

Mind, 1950

Краткое заключение по аппаратному обеспечению

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

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

Логическая структура компьютеров не сильно изменилась с 1940-х годов, но физическая конструкция преобразовалась колоссально. Уже больше пятидесяти лет подтверждается закон Мура – по сути, предсказание, повлиявшее на реальность. В нем говорится об экспоненциальном уменьшении размера и цены отдельных компонентов и, следовательно, экспоненциальном увеличении вычислительной мощности при заданной стоимости и объеме пространства. Предупреждения о том, что закон Мура перестанет действовать в ближайшие 10 лет, неизменно звучат в предсказаниях о развитии технологий вот уже на протяжении десятилетий[23]. Очевидно, что в наше время техпроцесс для интегральной схемы совершенствовать сложнее, поскольку компоненты теперь могут состоять всего из нескольких атомов. Но люди в прошлом выказывали удивительную изобретательность: возможно, какое-нибудь новое открытие обеспечит нам следующий взлет.

Цифровые устройства работают в двоичном формате. На базовом уровне информация представлена в компонентах с двумя состояниями, поскольку они просты в сборке и наиболее надежны в эксплуатации. Информация любого вида представлена в виде набора битов. Числа разных видов (целые, дробные, научная запись[24]) отображаются с помощью 1, 2, 4 или 8 байт – такие фрагменты естественным образом подходят для обработки аппаратным обеспечением. Это значит, что при обычных условиях числа обладают конечным размером и ограниченной точностью. С помощью подходящего ПО можно поддерживать произвольный размер и точность, хотя программы на таком обеспечении будут работать медленнее. Иную информацию, вроде символов естественных языков, также представляют в виде некоторого количества байтов. Шифрование ASCII, которое вполне годится для английского языка, задействует один байт на символ. Более обширный Unicode, сочетающий несколько кодировок, обрабатывает все наборы символов, но занимает ощутимо больше места. UTF-8 – кодировка переменной длины в Unicode, которая предназначена для обмена информацией между системами. В ней применяется один байт для символов из ASCII и два или более для других.

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

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

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

Часть II