Инструменты, алгоритмы и структуры данных

Немного об аппаратуре

Разбить на страницы
Показывать лекцию целиком

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

(рис 1.1) Компоненты компьютерной системы

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

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

1.1. Кодирование данных

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

Двоичная система счисления

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

В основе двоичной системы лежат два значения (отсюда "двоичная"). Сами значения не несут особого смысла и их можно называть по-разному: Белые и Черные, Чук и Гек или Изида и Озирис. Имеет значение лишь то, что они различны. Фактически мы будем называть их 0 и 1.

Термин "бит" означает математическую переменную, чьи возможные значения как раз и есть 0 и 1. Он придуман инженерами в конце 1940-го года как сокращение двух слов "binary digit", подчеркивающее, что бит подобен цифрам обычной арифметики (0, 1, … 9), но только с двумя возможными цифрами.

(рис 1.2) Бит (техническая версия)

Бит также обозначает техническое устройство с двумя возможными состояниями, следовательно, способное быть представленным математическим битом, стоит только договориться, что есть 0, а что 1. Флажок на кабинете доктора, который может находиться в одном из состояний: "доктор свободен" и "доктор занят" – является битом. Для компьютерной индустрии важную роль играют "электронные" биты, где два состояния соответствуют двум разным напряжениям или намагниченным и размагниченным участкам, как на магнитной ленте или диске.

Причина, по которой двоичная система так хорошо себя зарекомендовала в том, что электроника сделала возможным:

  • реализовать физические биты и упаковать их в небольшом объеме – если быть совсем точными, то очень много битов в очень маленькой области. Ниже мы увидим некоторые примеры;
  • быстро писать и читать биты. Очень быстро;
  • дешево создавать множество таких совокупностей битов. Очень дешево.
  • Эти свойства обеспечили успех двоичной системе. Некоторые первые компьютеры использовали десятичную систему. Тогда компьютеры рассматривались как машины для вычислений, и казалось естественным для вычислений применять привычную для людей десятичную систему, пришедшую еще с тех давних времен, когда для счета использовались пальцы рук, а пальцев было 10, а не 8, и не 16 (само слово digit – "цифра" – произошло от латинского слова finger – "палец"). Но для компьютеров, построенных на электронике, двоичная система давным-давно вытеснила всех своих соперниц.

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

    Основы двоичной системы

    Если информация, с которой вам приходится иметь дело, – нечто большее, чем результаты игры в "орёл или решка", то двух значений явно маловато. Базисные комбинации, которыми кодируются конечное множество данных любого размера, являются:

  • байт – последовательность из 8 битов;
  • слово (word) на новых компьютерах предполагает последовательность из 8 байтов или шестидесяти четырех битов. В прошлые два десятилетия слово обычно означало тридцать два бита, отсюда "64-битная архитектура" и "32-битная архитектура" компьютеров.
  • Ранее определение "слова" не было стандартизовано, и компьютеры использовали слова различной длины. До сих пор можно столкнуться с "отклонениями", но они редки. Байты всегда имели 8 битов и иногда называются октетами.

    Сколько различных значений может задавать последовательность битов? Один бит позволяет задавать два значения: 0 и 1. С двумя битами возможностей становится уже четыре:

    В общем случае, последовательность из n битов для любого целого n > 0 задает $$2^n$$ значений.

    Базисные представления и адреса

    Для базисных единиц:

  • байт с его 8-ю битами имеет 256 ($$2^8$$) различных значений;
  • слово из 32 битов имеет $$2^{32}$$ возможных значений, примерно 4 миллиарда, точное значение приведено в нижеследующей таблице.
  • Если, например, мы хотим хранить текст, то можно использовать один байт для хранения каждого символа текста. Может показаться, что 256 различных символов избыточно много для представления текста, но фактически это как раз то, что нужно, поскольку помимо 26 строчных и 26 заглавных букв латиницы необходимы цифры, специальные символы клавиатуры компьютера (такие как ~, !, @ и другие ), акцентированные символы для букв западных языков ($$\acute e$$, $$\ddot A$$) и так далее. Стандартное кодирование всех этих символов 8-битовым представлением известно как расширенный код ASCII (American Standard Code for Information Interchange). Оригинальный код ASCII использовал только 7 битов (128 значений) и не поддерживал акцентированные буквы.

    Расширенный ASCII код имеет несколько вариантов, наиболее популярный известен как стандарт ISO 8859-1, покрывающий символы наиболее распространенных европейских языков.

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

    Для представления символьных сущностей в Eiffel можно использовать тип CHARACTER_8 для расширенного ASCII или CHARACTER_32 для Unicode. Общим решением является применение типа CHARACTER, который приводится к тому или другому типу в зависимости от конфигурационных установок. С этим мы встретимся в примерах, включающих символьные типы.

    Для численной информации общеупотребительной практикой является использование слова для хранения целых значений. Математическое множество целых бесконечно, но память компьютера способна хранить только конечное множество. Если использовать для хранения целых чисел 32-битное слово, оно позволяет задавать примерно два миллиарда отрицательных целых и два миллиарда положительных целых чисел. Для многих приложений этого достаточно. С 64-мя битами границы существенно расширяются. В наших программах тип для целых именуется INTEGER. Целые характеризует и тип CHARACTER. В установках конфигурации целочисленный тип может быть определен как INTEGER_32 или INTEGER_64. Доступны в программах и типы INTEGER_8 и INTEGER_16. Если приходится иметь дело только с неотрицательными целыми, то можно использовать тип NATURAL с его вариантами от NATURAL_8 до NATURAL_64.

    Для представления чисел, отличных от целых, – рациональных, таких как 3/2, или иррациональных (вещественных), таких как π, используются типы REAL_32, REAL_64. Опять-таки можно использовать общий настраиваемый тип REAL. В отличие от целых чисел для представления вещественных значений обычно $$2^{32}$$ значений недостаточно, поэтому чаще для представления вещественных чисел задействуется тип REAL_64.

    Адресом элемента данных называется его позиция в пронумерованной памяти компьютера. Примеры типов данных: CHARACTER_8, INTEGER_32 и REAL_64 показывают, что элементы данных могут быть различных размеров (1, 4 или 8 байтов). Для обеспечения унификации в адресации памяти за единицу памяти принимается байт, а начальный адрес равен нулю. Так, если память начинается с размещения 1000 значений типа INTEGER_64 на компьютере с 8-байтными словами, то первый свободный элемент памяти имеет адрес 8000.

    Степени двойки

    Уже по одной той причине, что n битов могут хранить $$2^n$$ значений, степени двойки важны для двоичной системы. Ниже перечислены некоторые элементы этой последовательности.

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

    От вишен к байтам

    В обычном, десятичном способе счета, аббревиатура "кило" представляет степень 10, точнее, $$10^3$$, которая служит естественной мерой вещей. На рынке мы покупаем один килограмм вишен, равный тысяче – $$10^3$$ – грамм, за один миллион – $$10^6$$ – долларов вы едва ли купите приличный дом в Южной Калифорнии, один миллиард – $$10^9$$ – долларов может продлить на несколько часов существование банка, падающего во время кризиса.

    n $$2^n$$ Аппроксимация степенями 10 Общепринятое имя (аббревиатура) Официальное название (аббревиатура)
    0 0
    1 1
    2 4
    3 8
    4 16
    5 32
    6 64
    7 128
    8 256
    9 512
    10 1024 103 (тысяча) Kilo (K) Кило Kibi (Ki)
    16 65536
    20 1 048 576 106 (миллион) Mega (M) Мега Mebi (Mi)
    30 1 073 741 824 109 (миллиард) Giga (G) Гига Gibi (Gi)
    32 4 294 967 296 4*109 (4 миллиарда)
    40 1 099 511 627 776 1012(триллион) Tera (T) Тера Tebi (Ti)
    50 1 125 899 906 842 624 1015 Peta (P) Пета Pebi (Pi)
    64 18 446 744 073 709 551 616 1.8 x 1019

    Эти единицы применимы и к другим измерениям, связанным с компьютерами, не только по отношению к памяти.

  • Обмен данными по линии может проходить со скоростью в 1 Mbps (Megabit per second) – один Мегабит за секунду.
  • Центральный процессор может работать со скоростью в 1 GHz (Gigaherz – Гигагерц), выполняя один миллиард базисных операций процессора за секунду. "Герц" – термин, заимствованный у физики, единица измерения частоты.
  • В то время как размеры памяти и адресация выражается в байтах, скорость передачи данных обычно представлена в "битах за секунду", или bps. Так что "56К модем", если он работает на предельной для него скорости, что практически не случается, может передавать 56000 бит в секунду.

    Компьютерные инженеры предпочитают использовать степени двойки для выражения размеров памяти. Здесь и начинается путаница. Точнее, она началась, когда некто (имя его не сохранилось в истории) заметил, что два в десятой степени равно 1024, что примерно равно $$10^3$$ – тысяче, и как следствие принял блестящее решение использовать десятичные аббревиатуры – кило для почти-тысячи ($$2^{10}$$), мега для почти-миллиона ($$2^{20}$$), гига для почти-миллиарда ($$2^{30}$$). С ростом степеней аппроксимация становится все хуже, как видно из таблицы.

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

    Чтобы покончить с беспорядком, соответствующая организация, занимающаяся стандартами, предписала применять десятичные аббревиатуры – кило и другие – для степеней 10, а для степеней двойки применять другие аббревиатуры – киби, меби, гиби, приведенные в последнем столбце таблицы. Эти имена не получили широкого распространения.

    Откровенно говоря, в 2009 году их никто не использовал. Поиск в Google показал, что все ссылки на Gibibyte (Гибибайт) давали только определение термина, но не давали примеров его практического использования.

    Во избежание недоразумений помните, что двоичная интерпретация применяется только для измерения памяти, в остальных случаях применяется десятичная. Так что 1-GHz компьютер с памятью в 1 GB выполняет один миллиард операций в секунду, но памяти имеет больше чем один миллиард, – на 73 миллиона байтов больше. На практике разница не столь уж велика: что для нас десяток-другой миллионов?

    Действия над числами

    Представление целых чисел в компьютере хорошо тем, что оно точно. Действия над целыми выполняются точно так же, как это принято в математике. Плохо лишь то, что целые в компьютере составляют конечное подмножество, в отличие от математики. Для 64-битного компьютера множество целых определяется диапазоном: $$(-2^{63}, +2^{63} -1)$$.

    Точные значения границ диапазона зависят от представления отрицательных чисел в памяти компьютера. Обычно отрицательные числа хранятся в дополнительном коде. Тогда, если для хранения целого числа отводится N битов, то нижняя граница равна ($$-2^{N -1}$$), а верхняя ($$+2^{N -1} -1$$), так что максимальное по модулю отрицательное число на единицу больше максимального положительного числа. Дополнительный код строится так, чтобы поразрядное сложение n и –n в двоичной системе давало ноль. Для положительных целых старший разряд в представлении числа N битами является знаковым и равен нулю, так что на само число остается N-1 бит, что и дает верхнюю границу. Отрицательные числа строятся добавлением единицы к обращению положительного числа (под обращением понимается взаимная замена 0 и 1). При N = 4 число 5 будет храниться как 0101, а число -5 – как 1011.

    Для допустимых значений целых точным является не только их представление – результаты операций над целыми дают те же значения, что и их математические двойники, за исключением тех случаев, когда значения результатов выходят за пределы, допустимые для целых. Написав a + b для целых a и b, получим корректный результат, если он не больше максимального или не меньше минимального значения. Выход результата за допустимые пределы известен как "арифметическое переполнение".

    Представление вещественных чисел – REAL-тип, как он называется в Eiffel (float – в других языках), аналогично представлению целых в том отношении, что задает конечное множество возможных значений. Как следствие, представление вещественных чисел частично и не всегда точно. В математике между любыми двумя вещественными числами находится бесконечно много других вещественных чисел. Это же справедливо и для рациональных чисел – менее мощного подмножества вещественных чисел, так что даже все рациональные числа не могут быть точно представлены в памяти компьютера. В этом отражается типичная разница между информацией и хранимыми данными.

    Стандартное представление вещественного числа состоит из трех частей: бита s, задающего знак числа, целого n – порядка числа, вещественного f, называемого нормализованной мантиссой, которая задает дробную часть, чья старшая цифра отлична от нуля. Вещественное число имеет вид $$f\times{2^n}$$, со знаком s.

    Эти свойства отражаются в программе тремя способами.

  • Когда вещественное число x задается в программе явно программным текстом, или читается из файла, или поступает от датчиков или других устройств, то хранимое его значение является представимым значением вещественного типа, близкого к значению x, но не гарантированно с ним совпадающего.
  • Арифметические операции – сложение, умножение, вычитание, деление, возведение в степень – могут быть невыполнимыми, хотя математически их результат строго определен. Переполнение (Overflow), упоминаемое выше для целых, здесь также возможно. Если x и y – два представимых вещественных числа (предположим для определенности, что они положительны), то $$x + y$$ или $$x\times y$$ могут быть слишком большими для представления. Еще один пример, характерный уже только для вещественных чисел: деление $$x / y$$, где y не равно нулю, но мало, так что результат в математике определен, но слишком велик для представления в системе компьютера. Для вещественных чисел возможно возникновение ситуации, называемой "Переполнение снизу" (underflow), которая может возникать, например, при делении x / y, когда y слишком велико и результат слишком мал по абсолютной величине, чтобы он мог быть представлен значением, отличным от нуля (в этом случае говорят, что результат является "машинным" нулем).
  • Даже в отсутствие переполнений сверху и снизу арифметические операции могут стать причиной возникновения ошибок. Если x' и y' являются представимыми значениями x и y, то программистская нотация $$x + y$$ не обозначает математическую нотацию суммы x и y, она даже не может представлять точное значение суммы $$x' + y'$$, поскольку не гарантируется представимость результата. Любой алгоритм, имеющий дело с вещественными числами, должен принимать в расчет эти ограничения.
  • Об этом непрестанно приходится заботиться в "численных расчетах", используемых не только в научных или инженерных проектах, но и, например, в финансовом моделировании, где также применяются вещественные числа и численные алгоритмы. Ошибка в каждой операции незначительна и может быть совсем не страшной для результата. Плохо, когда эта ошибка накапливается в процессе выполнения миллионов и миллиардов операций, что в конечном итоге может привести к серьезным ошибочным следствиям и искажению результатов.

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

    $$\int\limits_{low}^{high}f(x)dx\approx\sum\limits_{i=0}^{n-1}f(low+i^*step)^*step,\qquad\text{где\;}n=\frac{high-low}{step}$$ (рис 1.3) Вычисление интеграла методом прямоугольников (конечная аппроксимация)

    Алгоритм может быть реализован циклом. Покажем сейчас фрагмент алгоритма, а его полная версия появится позже. Во фрагменте используется локальная переменная x типа REAL:

    from x := low until x >= high loop
        Result := Result + f.item ([x]) — f.item ([x])дает значение f(x).
        x:=x+step
    end
            

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

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

    from x := low until x >= high loop
        Result := Result + f.item ([x]) — f.item ([x]) дает значение f(x).
        i := i + 1 ; x := low + (i * step) - [2]
    end
            

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

    Почувствуй методологию

    Вычисления с вещественными числами

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

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

    В ненадежной стране численного программирования существует надежное место: стандартизация. Ранее каждая компьютерная система имела собственную систему представления чисел и операций, из-за чего нельзя было гарантировать, что математически корректный алгоритм будет корректно работать на компьютере. Ситуация исправилась с введением стандарта IEEE на архитектуру арифметики с плавающей точкой. Этот стандарт определяет единые рамки для системы чисел компьютеров как с 32-битной, так и с 64-битной архитектурой. Большинство современных компьютеров соответствуют этому стандарту. Так что, если нужно проверить, что алгоритм не является причиной появления неприемлемых вычислительных погрешностей, то такую работу нужно проделать только однажды, полагаясь на стандарт.

    1.2. О памяти

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

    Живучесть

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

  • Преходящая, кратковременная или оперативная, память хранит данные, создаваемые и обрабатываемые во время выполнения программы, но не гарантирует, что они сохранятся с окончанием этого сеанса выполнения. Технологически такая память при выключении электроэнергии теряет хранимые в ней данные.
  • Постоянная память, обеспечивающая живучесть данных, сохраняет данные и при выключении энергии, при условии, конечно, что данные не удаляются явным образом.
  • Зачем нужна кратковременная память? Не было бы проще, чтобы все данные хранились вечно по умолчанию? Есть две причины, по которым это не делается, – технологическая и экономическая. Память, доступная процессорам во время выполнения, должна быть очень быстрой и, как следствие, является дорогой. Постоянная память значительно дешевле, так что ее можно использовать для хранения больших объемов информации – текстов, рисунков, музыки, расписания полетов, персональных данных и т. д., доступ к которым более медленный.

    Слова: приемлемая или медленная скорость, большие или малые объемы, дорогая и дешевая – нельзя рассматривать вне контекста. Вот некоторые оценки (на момент написания оригинала курса).

  • Компьютер, подходящий для разработки ПО, возможно, ноутбук, может иметь оперативную память в несколько гигабайт стоимостью в несколько сотен долларов за память. Время доступа к символу составляет примерно 50 наносекунд, что позволяет за секунду получить 20 миллионов символов (одна наносекунда – ns – равна $$10^{-9}$$ секунды).
  • Компьютер может иметь диск (постоянную память) емкостью в сто раз больше оперативной памяти – в несколько сот гигабайт, стоящий вдвое дешевле, со скоростью доступа порядка 5 миллисекунд.
  • Как видите, время доступа к оперативной и постоянной памяти существенно различается, что непосредственно значимо для программиста. Программы, которые обрабатывают большие объемы данных, не могут игнорировать проблемы распределения и обмена данными между постоянной и оперативной памятью. Следует тщательно управлять временем передачи данных, чтобы сохранить приемлемое время выполнения приложения.

    Оперативная память

    Как отмечалось, операции процессора получают доступ к оперативной памяти. Этот ключевой компонент вычислений имеет несколько имен:

  • главная память;
  • первичная память;
  • RAM-память (Random Access Memory – память со случайным доступом). Последний термин имеет исторические корни. Вначале постоянная память, в отличие от первичной, была реализована технологически на магнитных лентах, а еще ранее – на перфорированных бумажных ленточках, где дырка в нужном месте задавала бит, равный 1, а отсутствие дырки означало, что значение бита равно 0. Доступ к данным на такой ленте был последовательным, так что время доступа к тому или иному биту зависело от его адреса; нужно было перемотать ленту, прежде чем прочесть значение бита. Оперативная память того времени была реализована как память прямого доступа. Время доступа к любому элементу не зависело от его адреса, поэтому такая память и получила название RAM-памяти. Теперь, конечно, и память на дисках является памятью прямого доступа, но название RAM все еще сохранилось для оперативной памяти;
  • ферритовая (Core) память. Термин опять восходит к старым технологиям, когда элементами памяти служили ферритовые намагниченные сердечники – ферритовые ядра, так что до сих пор можно услышать, что множество данных хранится "в ядре".
  • На ниже представленной фотографии показана главная память – чип (микросхема), на два GB, изготовленная по технологии "DDR2_800", обеспечивающая время цикла в 5 наносекунд с пиковой производительностью передачи данных в 6,4 гигабайта в секунду.

    (рис 1.4) Микросхема с оперативной памятью

    Разнообразие постоянной памяти

    Существует два вида постоянной памяти.

  • Некоторые элементы рассматриваются как часть компьютера и присоединены к нему постоянно. Они называются вторичной памятью, чтобы подчеркнуть тот факт, что они фактически являются продолжением первичной памяти. Их положительной стороной (помимо обеспечения живучести данных) является относительно низкая стоимость доступа, позволяющая сохранять сотни гигабайт. Обратная сторона медали – доступ значительно медленнее, чем к первичной памяти, и процессорам эта память непосредственно недоступна. Чтобы обработать данные, хранимые в этой памяти, они предварительно должны быть прочитаны в оперативную память.
  • Другие элементы памяти присоединяются к компьютеру эпизодически на момент копирования данных, а затем могут быть удалены. Позже они могут быть присоединены к этому же или к другому компьютеру. В частности, они служат для хранения "резервных копий" данных. Они также называются съемной памятью или сменными хранилищами (storage) – синонимами памяти.
  • Наиболее общей формой вторичной памяти является диск. Более корректный термин – дисковод или дисковое устройство, включающее несколько дисков на одном стержне, вращающихся в процессе работы со скоростью от 4000 до 12000 оборотов за секунду. Данные считываются или записываются специальными головками, которые могут перемещаться над рабочей поверхностью вращающихся дисков. Значение считываемых или записываемых битов зависит от намагничивания небольших областей диска. Если выключить энергию, то диск вращаться не будет и операции записи и чтения становятся невозможными, но намагничивание остается, что и гарантирует сохранность данных на диске.

    (рис 1.5) Дисковод

    Устройство, показанное на рисунке, имеет два диска, хотя вы видите только один. Оно может хранить 8 гигабайт (если это вас не впечатляет, то скажу, что это модель 1999 года, но я пока не намерен разобрать свой новейший дисковод, чтобы показать вам его фотографию. В магазине на момент написания этого текста нетрудно приобрести диск на несколько сот гигабайт стоимостью в 50$, терабайтные диски также вполне доступны). Дисковод на рисунке обеспечивал скорость вращения 5400 оборотов в секунду со временем доступа в 9 миллисекунд и максимальной скоростью передачи данных в 33 мегабайта за секунду. Время доступа и передачи указаны приблизительно, поскольку важной характеристикой является латентное время – время задержки, связанное с перемещением головки диска к нужной дорожке, после чего получить требуемые данные на дорожке можно много быстрее. Когда разрабатываются программы, в которых предусмотрена интенсивная работа с диском, нужно учитывать это свойство для оптимизации производительности.

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

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

    Портативный компьютер – лэптоп, MIT Media Lab's XO laptop, – введенный в 2007 году как часть проекта "Каждому ребенку – свой лэптоп" (OLPC project – One Laptop Per Child), был одним из первых компьютеров, спроектированных для широкого распространения в мире, и использовал флеш-память, а не диски.

    (рис 1.6) Лэптор OLPC работающий с EiffelStudio

    Следуя традициям бумажных лент, упомянутых ранее, некоторые устройства памяти являются съемными. Среди наиболее популярных являются USB-устройства, называемые так потому, что они связаны со стандартизованной последовательной шиной для передачи данных (Universal Serial Bus). Они используют технологию флеш-памяти емкостью от 2-х до 16 гигабайт, а теперь и больше. Съемными теперь являются и USB-диски, устроенные так же, как и встроенные диски, но присоединяемые к USB-порту, что и обеспечивает возможность их смены. Емкость таких дисков начинается от 100 GB.

    (рис 1.7) "Флэшка" и USB-диск

    Регистры и иерархия памяти

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

    a := a + b
            

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

    Как результат, базисная иерархия памяти имеет три уровня:

  • регистры, число которых мало, но скорость сравнима со скоростью центрального процессора;
  • первичная память, ядро, емкостью в несколько гигабайт, но более медленная;
  • диск или его эквивалент в несколько сот гигабайт емкостью, но еще более медленный.
  • Типичный порядок времени записи к моменту написания этого текста составлял: 0,5 наносекунды, 50 наносекунд и 5 миллисекунд. Цифры могут со временем изменяться довольно быстро, но порядок отношения обычно остается. Рассмотрим в частности отношение между двумя последними видами памяти, примерно равное 100000. Спроецируем эти отношения на человеческий уровень выполнения работ. Вообразите рабочего, который:

  • способен обработать деталь, если она уже находится в нужном месте, за 0.01 секунды (аналог операций процессора, когда операнды помещены в регистры);
  • получать заготовку из рядом расположенного хранилища, тратя на это одну секунду (аналог главной памяти, большой, но все же ограниченной);
  • получать неограниченное количество заготовок от поставщика, удаленного за сотни километров, так что на доставку уходят целые сутки.
  • Для компьютера все цифры нужно делить на 20 000 000, но соотношение остается тем же самым. Политика управления памятью – что хранить в оперативной памяти, что на диске – существенно влияет на производительность.

    Виртуальная память

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

    Виртуальная память предоставляет программисту адресное пространство, значительно превосходящее физическое адресное пространство. Система управления памятью, стоящая за сценой, обеспечивает подкачку нужных данных с диска, когда они требуются, но не находятся в первичной памяти. Технически вся память разделяется на единицы, называемые страницами, каждая обычно размером в несколько килобайт. Фактический доступ к данным требует, чтобы они были в ядре, если это не так, то возникает ошибка доступа к странице, система виртуальной памяти в этом случае загружает соответствующую страницу из диска – эта операция называется "загрузка страницы" (page in). При загрузке страницы может возникнуть ситуация, когда физическая память использована и для страницы уже нет места. В этом случае предварительно произойдет "выгрузка страницы" на диск (page out). Есть разные сценарии, позволяющие выгружать страницы, которые с большой долей вероятности не понадобятся в ближайшее время.

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

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

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

    1.3. Команды компьютера

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

    Типичная команда компьютера хранится в слове памяти, иногда в нескольких словах. Она содержит код команды, определяющий ее тип – операцию, выполняемую командой, а также ноль или более требуемых аргументов, которые могут быть адресами или значениями. Для примера рассмотрим команду компьютера с архитектурой 32-битного Power PC. Команда занимает слово (32 бита) с нумерацией битов, начинающейся с нуля:

    Код команды является комбинацией первичного кода 31 (двоичное 11111), заданного битами от 0 до 5, и вторичного кода 266 (двоичное 100001010) в битах от 22 до 31. Результат помещается в регистр 5 (двоичный код 101), а операнды читаются из регистров 3 и 4.

    Побитовое представление неудобно для практического использования человеком. Обычная нотация заменяет двоичное представление более коротким – шестнадцатеричным (группируя биты в четверки, которые заменяются одной шестнадцатеричной цифрой) или восьмеричным (биты группируются тройками). Выше представленное слово 01111100101000110010000100001010 равно 7CA3210A в шестнадцатеричной системе, где буквы от A до F являются цифрами со значениями от 10 до 15.

    Компьютеры обладают командами трех разных типов.

  • Вычисления: арифметические операции, такие как сложение (в приведенном примере), вычитание, умножение, деление с вариантами для целых и вещественных чисел, операции сравнения, побитовые операции, которые выполняют логические операции над соответствующими парами битов двух слов. Обычно такие операции выполняются над операндами, расположенными в регистрах.
  • Загрузки и хранения: передают данные из оперативной памяти в регистры и обратно, а также обеспечивают передачу данных с другими устройствами аппаратуры.
  • Управления: переключая выполнение к другой точке программного кода в зависимости от выполнения некоторого условия или осуществляя безусловный переход.
  • Каждая команда имеет свой код, такой, как 31 в примере. Более удобно ссылаться на код команды, используя мнемонику. Для Power PC мнемоника команды сложения с кодом 31 – add. Представление программы в машинных командах не является удобной формой для общения с человеком. Язык ассемблера обеспечивает приемлемую, доступную для восприятия человеком форму представления таких программ. На языке ассемблера Power PC рассмотренная нами команда может быть записана так:

    add r5, r3, r4
        

    Язык ассемблера заимствовал у языка программирования возможность применения символических имен: имя регистра, такое как r5, имя команды, такое как add, – так же как использование идентификаторов для адресов и констант. Ответственность за трансляцию ассемблерного кода в машинный код ложится на программную систему, называемую ассемблером, которая представляет простейший вид компилятора. Трансляция облегчается тем, что для языка ассемблера выполняется соответствие "один в один" – одна команда ассемблера, как правило, транслируется в одну машинную команду компьютера. Операторы языка программирования задают куда более высокий уровень абстракции. Еще одна особенность ассемблера состоит в том, что для каждой архитектуры компьютера есть свой язык ассемблера, связанный с этой архитектурой. Современные языки программирования являются переносимыми (независимыми от платформы). Не будучи в полном смысле этого термина языком программирования, язык ассемблера позволяет преодолеть наиболее утомительные моменты, связанные с написанием и чтением программ в машинном коде.

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

    1.4. Закон Мура и эволюция компьютеров

    При рассмотрении влияния производительности аппаратуры на программирование нельзя не учитывать ось времени. Чрезвычайные успехи информационных технологий идут рука об руку с прогрессом в разработке аппаратуры. В 1965 году появилась статья Гордона Е. Мура (Gordon Moore), сооснователя корпорации Intel, в которой он в чрезвычайно яркой форме описал этот феномен. Наибольшую известность получил так называемый "закон Мура", формулируемый следующим образом: "Число компонентов, размещаемых на интегральной схеме при сохранении постоянной стоимости, удваивается каждые 18 месяцев" (заметим, что сам Мур говорил о двух годах, но потом эта константа была уменьшена по результатам наблюдений до полутора лет). Есть несколько вариантов этого закона, но все они говорят об экспоненциальном росте. Эти утверждения не являются в полном смысле этого слова "законами", такими, как законы, открытые Максвеллом, Ньютоном или Эйнштейном. Это наблюдения о прогрессе индустрии в течение нескольких десятилетий – наблюдения, которые оказались пророческими и до сих пор продолжают быть применимыми. Удивительно, что нет никакой другой области человеческой деятельности, хоть сколько-либо напоминающей столь удивительную скорость роста. Автомобили сегодня не в тысячу раз быстрее, чем 20 лет назад.

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

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

    1.5. Дальнейшее чтение

    Стандарт IEEE для арифметики с плавающей точкой, доступный по адресу: ieeexplore.ieee.org/xpl/freeabs_all.jsp?arnumber=4610935

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

    John Markoff: .

    Не являясь научной публикацией, эта статья дает ясное описание необходимости параллельной архитектуры для поддержания закона Мура. Описывает трудности параллельного программирования. В течение многих лет Джон Марков был корреспондентом газеты "Нью-Йорк Таймс" в Силиконовой долине и играл важную роль в индустрии.

    John L. Hennessy and David Patterson; Computer Architecture, Fourth Edition: A Quantitative Approach, Morgan Kauffmann, 2006.

    Классический учебник по архитектуре компьютеров. Дополнения в последнем издании отражают последние тенденции, в частности, переход к параллельной архитектуре. (рис 1.8) Дэвид Паттерсон (2007)

    1.6. Ключевые концепции, изученные в этой лекции

  • Внутреннее представление данных в компьютере использует двоичную систему.
  • Базисной единицей данных является бит с двумя возможными значениями: 0 и 1. Биты группируются в байты, содержащие 8 битов, и слова, обычно из 8-ми или 4-х байтов (64-битная или 32-битная архитектура). Адреса измеряются в байтах. Целые и вещественные числа обычно хранятся в словах. Существуют также более компактные представления из одного или двух байтов. Символы представлены одним байтом (расширенный ASCII) или двумя байтами (Unicode).
  • Измерение величин, отличных от единиц памяти, например скорости, всегда использует десятичные единицы, такие как кило (тысяча), мега (миллион), гига (миллиард), тера ($$10^{12}$$), пета ($$10^{15}$$). Для описания размеров памяти и адресов общей практикой, несмотря на официальные стандарты, принято использовать те же префиксы (кило и другие) для степеней двойки, начиная с $$2^{10} = 1024$$ и примерно равное $$10^3 = 1000$$.
  • Представление целых в компьютере является точным, но только в конечном интервале.
  • Представление вещественных чисел является приближенным. Арифметические операции являются обычно источником появления погрешностей. Реализация численных алгоритмов должна избегать накопления таких ошибок.
  • Иерархия памяти включает регистры, оперативную память и устройства постоянной памяти, такие как диски и флеш-память. Операции процессора применимы к операндам, хранимым в регистрах. Доступ к регистрам – самый быстрый (менее наносекунды), но число регистров невелико. Оперативная память на сегодняшний день имеет порядок нескольких гигабайт со временем доступа примерно в 100 раз более медленным, чем доступ к регистрам. Эта память при отключении от источника питания не сохраняет значения данных. Внешняя память – диски, флеш – в сто тысяч раз медленнее оперативной памяти, но существенно превосходит ее по объему, от сотен гигабайт до терабайтов, обеспечивая сохранность хранимых в ней данных.
  • Машинный код представляет систему команд компьютера – операции нижнего уровня, непосредственно выполняемые компьютером, – вычисления над операндами в регистрах, обмен данными между разными уровнями памяти, передачи управления командам.
  • Для компьютерной индустрии характерен экспоненциальный рост, известный как закон Мура. Поддержание этой тенденции потребовало перехода к многоядерным процессорам и параллельному программированию.
  • Новый словарь

    Address Адрес Bit Бит
    Byte Байт Core Ядро (Первичная память)
    Disk Диск Flash memory Флеш-память
    Giga Гига Hexadecimal Шестнадцатеричный
    Kilo Кило Mega Мега
    Moore's law Закон Мура Multicore (and manycore) Многоядерный
    Octal Восьмеричный Persistent Живучий (сохраняемый)
    RAM RAM-память прямого доступа Read Чтение
    Register Регистр Removable memory Сменная память
    Primary memory Первичная (оперативная) память Secondary memory Вторичная память
    Storage Хранилище Transient Кратковременный
    Word Слово Write Запись

    1.7. Упражнения

    1.7.1. Словарь

    Дайте точные определения терминам словаря.

    1.7.2. Карта концепций

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

    1.7.3 Измерения

    Сколько байтов в:

  • килобайте
  • мегабайте
  • мегаслове (слово = 4 байта)
  • гигабайте?
  • 1.7.4. Ваш новый лэптоп

    Каталог рекламирует лэптоп с 1,3 GB памяти.

  • Сколько байтов содержит эта память?
  • Сколько битов содержит эта память?
  • Предположим, что вся память используется для представления одной переменной. Сколько возможных значений может иметь эта переменная? Не требуется выписать точное число (подсказка: не пытайтесь это сделать, если только вы не являетесь владельцем бумажной фабрики; приведите аппроксимацию в форме $$10^n$$).
  • Если бы вы захотели написать это число на бумаге, 100 цифр в строке и 60 строк на странице, то сколько страниц вам бы потребовалось?
  • 1.7.5. Размер и скорость передачи

    Необходимо передать 128 MB данных, используя 128 Mb модем, работающий с максимальной скоростью. Сколько секунд это займет?

    1.7.6. Восьмеричная арифметика

    Восьмеричная арифметика использует систему с основанием 8 и цифрами от 0 до 7.

  • Запишите десятичное число 300000 в восьмеричной системе.
  • Число 74223 в восьмеричной системе запишите как десятичное число.
  • Вычислите сумму этих двух чисел, используя восьмеричную арифметику. Представьте результат в обеих системах.
  • 1.7.7. Шестнадцатеричная арифметика

    Шестнадцатеричная арифметика использует систему с основанием 16 и цифрами от 0 до 9 и A до F.

  • Запишите десятичное число 300000 в шестнадцатеричной системе.
  • Число A42D3 в шестнадцатеричной системе запишите как десятичное число.
  • Вычислите сумму этих двух чисел, используя шестнадцатеричную арифметику. Представьте результат в обеих системах.
  • Страницы:

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

    (рис 1.1) Компоненты компьютерной системы

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

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

    1.1. Кодирование данных

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

    Двоичная система счисления

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

    В основе двоичной системы лежат два значения (отсюда "двоичная"). Сами значения не несут особого смысла и их можно называть по-разному: Белые и Черные, Чук и Гек или Изида и Озирис. Имеет значение лишь то, что они различны. Фактически мы будем называть их 0 и 1.

    Термин "бит" означает математическую переменную, чьи возможные значения как раз и есть 0 и 1. Он придуман инженерами в конце 1940-го года как сокращение двух слов "binary digit", подчеркивающее, что бит подобен цифрам обычной арифметики (0, 1, … 9), но только с двумя возможными цифрами.

    (рис 1.2) Бит (техническая версия)

    Бит также обозначает техническое устройство с двумя возможными состояниями, следовательно, способное быть представленным математическим битом, стоит только договориться, что есть 0, а что 1. Флажок на кабинете доктора, который может находиться в одном из состояний: "доктор свободен" и "доктор занят" – является битом. Для компьютерной индустрии важную роль играют "электронные" биты, где два состояния соответствуют двум разным напряжениям или намагниченным и размагниченным участкам, как на магнитной ленте или диске.

    Причина, по которой двоичная система так хорошо себя зарекомендовала в том, что электроника сделала возможным:

  • реализовать физические биты и упаковать их в небольшом объеме – если быть совсем точными, то очень много битов в очень маленькой области. Ниже мы увидим некоторые примеры;
  • быстро писать и читать биты. Очень быстро;
  • дешево создавать множество таких совокупностей битов. Очень дешево.
  • Эти свойства обеспечили успех двоичной системе. Некоторые первые компьютеры использовали десятичную систему. Тогда компьютеры рассматривались как машины для вычислений, и казалось естественным для вычислений применять привычную для людей десятичную систему, пришедшую еще с тех давних времен, когда для счета использовались пальцы рук, а пальцев было 10, а не 8, и не 16 (само слово digit – "цифра" – произошло от латинского слова finger – "палец"). Но для компьютеров, построенных на электронике, двоичная система давным-давно вытеснила всех своих соперниц.

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

    Основы двоичной системы

    Если информация, с которой вам приходится иметь дело, – нечто большее, чем результаты игры в "орёл или решка", то двух значений явно маловато. Базисные комбинации, которыми кодируются конечное множество данных любого размера, являются:

  • байт – последовательность из 8 битов;
  • слово (word) на новых компьютерах предполагает последовательность из 8 байтов или шестидесяти четырех битов. В прошлые два десятилетия слово обычно означало тридцать два бита, отсюда "64-битная архитектура" и "32-битная архитектура" компьютеров.
  • Ранее определение "слова" не было стандартизовано, и компьютеры использовали слова различной длины. До сих пор можно столкнуться с "отклонениями", но они редки. Байты всегда имели 8 битов и иногда называются октетами.

    Сколько различных значений может задавать последовательность битов? Один бит позволяет задавать два значения: 0 и 1. С двумя битами возможностей становится уже четыре:

    В общем случае, последовательность из n битов для любого целого n > 0 задает $$2^n$$ значений.

    Базисные представления и адреса

    Для базисных единиц:

  • байт с его 8-ю битами имеет 256 ($$2^8$$) различных значений;
  • слово из 32 битов имеет $$2^{32}$$ возможных значений, примерно 4 миллиарда, точное значение приведено в нижеследующей таблице.
  • Если, например, мы хотим хранить текст, то можно использовать один байт для хранения каждого символа текста. Может показаться, что 256 различных символов избыточно много для представления текста, но фактически это как раз то, что нужно, поскольку помимо 26 строчных и 26 заглавных букв латиницы необходимы цифры, специальные символы клавиатуры компьютера (такие как ~, !, @ и другие ), акцентированные символы для букв западных языков ($$\acute e$$, $$\ddot A$$) и так далее. Стандартное кодирование всех этих символов 8-битовым представлением известно как расширенный код ASCII (American Standard Code for Information Interchange). Оригинальный код ASCII использовал только 7 битов (128 значений) и не поддерживал акцентированные буквы.

    Расширенный ASCII код имеет несколько вариантов, наиболее популярный известен как стандарт ISO 8859-1, покрывающий символы наиболее распространенных европейских языков.

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

    Для представления символьных сущностей в Eiffel можно использовать тип CHARACTER_8 для расширенного ASCII или CHARACTER_32 для Unicode. Общим решением является применение типа CHARACTER, который приводится к тому или другому типу в зависимости от конфигурационных установок. С этим мы встретимся в примерах, включающих символьные типы.

    Для численной информации общеупотребительной практикой является использование слова для хранения целых значений. Математическое множество целых бесконечно, но память компьютера способна хранить только конечное множество. Если использовать для хранения целых чисел 32-битное слово, оно позволяет задавать примерно два миллиарда отрицательных целых и два миллиарда положительных целых чисел. Для многих приложений этого достаточно. С 64-мя битами границы существенно расширяются. В наших программах тип для целых именуется INTEGER. Целые характеризует и тип CHARACTER. В установках конфигурации целочисленный тип может быть определен как INTEGER_32 или INTEGER_64. Доступны в программах и типы INTEGER_8 и INTEGER_16. Если приходится иметь дело только с неотрицательными целыми, то можно использовать тип NATURAL с его вариантами от NATURAL_8 до NATURAL_64.

    Для представления чисел, отличных от целых, – рациональных, таких как 3/2, или иррациональных (вещественных), таких как π, используются типы REAL_32, REAL_64. Опять-таки можно использовать общий настраиваемый тип REAL. В отличие от целых чисел для представления вещественных значений обычно $$2^{32}$$ значений недостаточно, поэтому чаще для представления вещественных чисел задействуется тип REAL_64.

    Адресом элемента данных называется его позиция в пронумерованной памяти компьютера. Примеры типов данных: CHARACTER_8, INTEGER_32 и REAL_64 показывают, что элементы данных могут быть различных размеров (1, 4 или 8 байтов). Для обеспечения унификации в адресации памяти за единицу памяти принимается байт, а начальный адрес равен нулю. Так, если память начинается с размещения 1000 значений типа INTEGER_64 на компьютере с 8-байтными словами, то первый свободный элемент памяти имеет адрес 8000.

    Степени двойки

    Уже по одной той причине, что n битов могут хранить $$2^n$$ значений, степени двойки важны для двоичной системы. Ниже перечислены некоторые элементы этой последовательности.

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

    От вишен к байтам

    В обычном, десятичном способе счета, аббревиатура "кило" представляет степень 10, точнее, $$10^3$$, которая служит естественной мерой вещей. На рынке мы покупаем один килограмм вишен, равный тысяче – $$10^3$$ – грамм, за один миллион – $$10^6$$ – долларов вы едва ли купите приличный дом в Южной Калифорнии, один миллиард – $$10^9$$ – долларов может продлить на несколько часов существование банка, падающего во время кризиса.

    n $$2^n$$ Аппроксимация степенями 10 Общепринятое имя (аббревиатура) Официальное название (аббревиатура)
    0 0
    1 1
    2 4
    3 8
    4 16
    5 32
    6 64
    7 128
    8 256
    9 512
    10 1024 103 (тысяча) Kilo (K) Кило Kibi (Ki)
    16 65536
    20 1 048 576 106 (миллион) Mega (M) Мега Mebi (Mi)
    30 1 073 741 824 109 (миллиард) Giga (G) Гига Gibi (Gi)
    32 4 294 967 296 4*109 (4 миллиарда)
    40 1 099 511 627 776 1012(триллион) Tera (T) Тера Tebi (Ti)
    50 1 125 899 906 842 624 1015 Peta (P) Пета Pebi (Pi)
    64 18 446 744 073 709 551 616 1.8 x 1019

    Эти единицы применимы и к другим измерениям, связанным с компьютерами, не только по отношению к памяти.

  • Обмен данными по линии может проходить со скоростью в 1 Mbps (Megabit per second) – один Мегабит за секунду.
  • Центральный процессор может работать со скоростью в 1 GHz (Gigaherz – Гигагерц), выполняя один миллиард базисных операций процессора за секунду. "Герц" – термин, заимствованный у физики, единица измерения частоты.
  • В то время как размеры памяти и адресация выражается в байтах, скорость передачи данных обычно представлена в "битах за секунду", или bps. Так что "56К модем", если он работает на предельной для него скорости, что практически не случается, может передавать 56000 бит в секунду.

    Компьютерные инженеры предпочитают использовать степени двойки для выражения размеров памяти. Здесь и начинается путаница. Точнее, она началась, когда некто (имя его не сохранилось в истории) заметил, что два в десятой степени равно 1024, что примерно равно $$10^3$$ – тысяче, и как следствие принял блестящее решение использовать десятичные аббревиатуры – кило для почти-тысячи ($$2^{10}$$), мега для почти-миллиона ($$2^{20}$$), гига для почти-миллиарда ($$2^{30}$$). С ростом степеней аппроксимация становится все хуже, как видно из таблицы.

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

    Чтобы покончить с беспорядком, соответствующая организация, занимающаяся стандартами, предписала применять десятичные аббревиатуры – кило и другие – для степеней 10, а для степеней двойки применять другие аббревиатуры – киби, меби, гиби, приведенные в последнем столбце таблицы. Эти имена не получили широкого распространения.

    Откровенно говоря, в 2009 году их никто не использовал. Поиск в Google показал, что все ссылки на Gibibyte (Гибибайт) давали только определение термина, но не давали примеров его практического использования.

    Во избежание недоразумений помните, что двоичная интерпретация применяется только для измерения памяти, в остальных случаях применяется десятичная. Так что 1-GHz компьютер с памятью в 1 GB выполняет один миллиард операций в секунду, но памяти имеет больше чем один миллиард, – на 73 миллиона байтов больше. На практике разница не столь уж велика: что для нас десяток-другой миллионов?

    Действия над числами

    Представление целых чисел в компьютере хорошо тем, что оно точно. Действия над целыми выполняются точно так же, как это принято в математике. Плохо лишь то, что целые в компьютере составляют конечное подмножество, в отличие от математики. Для 64-битного компьютера множество целых определяется диапазоном: $$(-2^{63}, +2^{63} -1)$$.

    Точные значения границ диапазона зависят от представления отрицательных чисел в памяти компьютера. Обычно отрицательные числа хранятся в дополнительном коде. Тогда, если для хранения целого числа отводится N битов, то нижняя граница равна ($$-2^{N -1}$$), а верхняя ($$+2^{N -1} -1$$), так что максимальное по модулю отрицательное число на единицу больше максимального положительного числа. Дополнительный код строится так, чтобы поразрядное сложение n и –n в двоичной системе давало ноль. Для положительных целых старший разряд в представлении числа N битами является знаковым и равен нулю, так что на само число остается N-1 бит, что и дает верхнюю границу. Отрицательные числа строятся добавлением единицы к обращению положительного числа (под обращением понимается взаимная замена 0 и 1). При N = 4 число 5 будет храниться как 0101, а число -5 – как 1011.

    Для допустимых значений целых точным является не только их представление – результаты операций над целыми дают те же значения, что и их математические двойники, за исключением тех случаев, когда значения результатов выходят за пределы, допустимые для целых. Написав a + b для целых a и b, получим корректный результат, если он не больше максимального или не меньше минимального значения. Выход результата за допустимые пределы известен как "арифметическое переполнение".

    Представление вещественных чисел – REAL-тип, как он называется в Eiffel (float – в других языках), аналогично представлению целых в том отношении, что задает конечное множество возможных значений. Как следствие, представление вещественных чисел частично и не всегда точно. В математике между любыми двумя вещественными числами находится бесконечно много других вещественных чисел. Это же справедливо и для рациональных чисел – менее мощного подмножества вещественных чисел, так что даже все рациональные числа не могут быть точно представлены в памяти компьютера. В этом отражается типичная разница между информацией и хранимыми данными.

    Стандартное представление вещественного числа состоит из трех частей: бита s, задающего знак числа, целого n – порядка числа, вещественного f, называемого нормализованной мантиссой, которая задает дробную часть, чья старшая цифра отлична от нуля. Вещественное число имеет вид $$f\times{2^n}$$, со знаком s.

    Эти свойства отражаются в программе тремя способами.

  • Когда вещественное число x задается в программе явно программным текстом, или читается из файла, или поступает от датчиков или других устройств, то хранимое его значение является представимым значением вещественного типа, близкого к значению x, но не гарантированно с ним совпадающего.
  • Арифметические операции – сложение, умножение, вычитание, деление, возведение в степень – могут быть невыполнимыми, хотя математически их результат строго определен. Переполнение (Overflow), упоминаемое выше для целых, здесь также возможно. Если x и y – два представимых вещественных числа (предположим для определенности, что они положительны), то $$x + y$$ или $$x\times y$$ могут быть слишком большими для представления. Еще один пример, характерный уже только для вещественных чисел: деление $$x / y$$, где y не равно нулю, но мало, так что результат в математике определен, но слишком велик для представления в системе компьютера. Для вещественных чисел возможно возникновение ситуации, называемой "Переполнение снизу" (underflow), которая может возникать, например, при делении x / y, когда y слишком велико и результат слишком мал по абсолютной величине, чтобы он мог быть представлен значением, отличным от нуля (в этом случае говорят, что результат является "машинным" нулем).
  • Даже в отсутствие переполнений сверху и снизу арифметические операции могут стать причиной возникновения ошибок. Если x' и y' являются представимыми значениями x и y, то программистская нотация $$x + y$$ не обозначает математическую нотацию суммы x и y, она даже не может представлять точное значение суммы $$x' + y'$$, поскольку не гарантируется представимость результата. Любой алгоритм, имеющий дело с вещественными числами, должен принимать в расчет эти ограничения.
  • Об этом непрестанно приходится заботиться в "численных расчетах", используемых не только в научных или инженерных проектах, но и, например, в финансовом моделировании, где также применяются вещественные числа и численные алгоритмы. Ошибка в каждой операции незначительна и может быть совсем не страшной для результата. Плохо, когда эта ошибка накапливается в процессе выполнения миллионов и миллиардов операций, что в конечном итоге может привести к серьезным ошибочным следствиям и искажению результатов.

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

    $$\int\limits_{low}^{high}f(x)dx\approx\sum\limits_{i=0}^{n-1}f(low+i^*step)^*step,\qquad\text{где\;}n=\frac{high-low}{step}$$ (рис 1.3) Вычисление интеграла методом прямоугольников (конечная аппроксимация)

    Алгоритм может быть реализован циклом. Покажем сейчас фрагмент алгоритма, а его полная версия появится позже. Во фрагменте используется локальная переменная x типа REAL:

    from x := low until x >= high loop
        Result := Result + f.item ([x]) — f.item ([x])дает значение f(x).
        x:=x+step
    end
            

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

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

    from x := low until x >= high loop
        Result := Result + f.item ([x]) — f.item ([x]) дает значение f(x).
        i := i + 1 ; x := low + (i * step) - [2]
    end
            

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

    Почувствуй методологию

    Вычисления с вещественными числами

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

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

    В ненадежной стране численного программирования существует надежное место: стандартизация. Ранее каждая компьютерная система имела собственную систему представления чисел и операций, из-за чего нельзя было гарантировать, что математически корректный алгоритм будет корректно работать на компьютере. Ситуация исправилась с введением стандарта IEEE на архитектуру арифметики с плавающей точкой. Этот стандарт определяет единые рамки для системы чисел компьютеров как с 32-битной, так и с 64-битной архитектурой. Большинство современных компьютеров соответствуют этому стандарту. Так что, если нужно проверить, что алгоритм не является причиной появления неприемлемых вычислительных погрешностей, то такую работу нужно проделать только однажды, полагаясь на стандарт.

    1.2. О памяти

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

    Живучесть

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

  • Преходящая, кратковременная или оперативная, память хранит данные, создаваемые и обрабатываемые во время выполнения программы, но не гарантирует, что они сохранятся с окончанием этого сеанса выполнения. Технологически такая память при выключении электроэнергии теряет хранимые в ней данные.
  • Постоянная память, обеспечивающая живучесть данных, сохраняет данные и при выключении энергии, при условии, конечно, что данные не удаляются явным образом.
  • Зачем нужна кратковременная память? Не было бы проще, чтобы все данные хранились вечно по умолчанию? Есть две причины, по которым это не делается, – технологическая и экономическая. Память, доступная процессорам во время выполнения, должна быть очень быстрой и, как следствие, является дорогой. Постоянная память значительно дешевле, так что ее можно использовать для хранения больших объемов информации – текстов, рисунков, музыки, расписания полетов, персональных данных и т. д., доступ к которым более медленный.

    Слова: приемлемая или медленная скорость, большие или малые объемы, дорогая и дешевая – нельзя рассматривать вне контекста. Вот некоторые оценки (на момент написания оригинала курса).

  • Компьютер, подходящий для разработки ПО, возможно, ноутбук, может иметь оперативную память в несколько гигабайт стоимостью в несколько сотен долларов за память. Время доступа к символу составляет примерно 50 наносекунд, что позволяет за секунду получить 20 миллионов символов (одна наносекунда – ns – равна $$10^{-9}$$ секунды).
  • Компьютер может иметь диск (постоянную память) емкостью в сто раз больше оперативной памяти – в несколько сот гигабайт, стоящий вдвое дешевле, со скоростью доступа порядка 5 миллисекунд.
  • Как видите, время доступа к оперативной и постоянной памяти существенно различается, что непосредственно значимо для программиста. Программы, которые обрабатывают большие объемы данных, не могут игнорировать проблемы распределения и обмена данными между постоянной и оперативной памятью. Следует тщательно управлять временем передачи данных, чтобы сохранить приемлемое время выполнения приложения.

    Оперативная память

    Как отмечалось, операции процессора получают доступ к оперативной памяти. Этот ключевой компонент вычислений имеет несколько имен:

  • главная память;
  • первичная память;
  • RAM-память (Random Access Memory – память со случайным доступом). Последний термин имеет исторические корни. Вначале постоянная память, в отличие от первичной, была реализована технологически на магнитных лентах, а еще ранее – на перфорированных бумажных ленточках, где дырка в нужном месте задавала бит, равный 1, а отсутствие дырки означало, что значение бита равно 0. Доступ к данным на такой ленте был последовательным, так что время доступа к тому или иному биту зависело от его адреса; нужно было перемотать ленту, прежде чем прочесть значение бита. Оперативная память того времени была реализована как память прямого доступа. Время доступа к любому элементу не зависело от его адреса, поэтому такая память и получила название RAM-памяти. Теперь, конечно, и память на дисках является памятью прямого доступа, но название RAM все еще сохранилось для оперативной памяти;
  • ферритовая (Core) память. Термин опять восходит к старым технологиям, когда элементами памяти служили ферритовые намагниченные сердечники – ферритовые ядра, так что до сих пор можно услышать, что множество данных хранится "в ядре".
  • На ниже представленной фотографии показана главная память – чип (микросхема), на два GB, изготовленная по технологии "DDR2_800", обеспечивающая время цикла в 5 наносекунд с пиковой производительностью передачи данных в 6,4 гигабайта в секунду.

    (рис 1.4) Микросхема с оперативной памятью

    Разнообразие постоянной памяти

    Существует два вида постоянной памяти.

  • Некоторые элементы рассматриваются как часть компьютера и присоединены к нему постоянно. Они называются вторичной памятью, чтобы подчеркнуть тот факт, что они фактически являются продолжением первичной памяти. Их положительной стороной (помимо обеспечения живучести данных) является относительно низкая стоимость доступа, позволяющая сохранять сотни гигабайт. Обратная сторона медали – доступ значительно медленнее, чем к первичной памяти, и процессорам эта память непосредственно недоступна. Чтобы обработать данные, хранимые в этой памяти, они предварительно должны быть прочитаны в оперативную память.
  • Другие элементы памяти присоединяются к компьютеру эпизодически на момент копирования данных, а затем могут быть удалены. Позже они могут быть присоединены к этому же или к другому компьютеру. В частности, они служат для хранения "резервных копий" данных. Они также называются съемной памятью или сменными хранилищами (storage) – синонимами памяти.
  • Наиболее общей формой вторичной памяти является диск. Более корректный термин – дисковод или дисковое устройство, включающее несколько дисков на одном стержне, вращающихся в процессе работы со скоростью от 4000 до 12000 оборотов за секунду. Данные считываются или записываются специальными головками, которые могут перемещаться над рабочей поверхностью вращающихся дисков. Значение считываемых или записываемых битов зависит от намагничивания небольших областей диска. Если выключить энергию, то диск вращаться не будет и операции записи и чтения становятся невозможными, но намагничивание остается, что и гарантирует сохранность данных на диске.

    (рис 1.5) Дисковод

    Устройство, показанное на рисунке, имеет два диска, хотя вы видите только один. Оно может хранить 8 гигабайт (если это вас не впечатляет, то скажу, что это модель 1999 года, но я пока не намерен разобрать свой новейший дисковод, чтобы показать вам его фотографию. В магазине на момент написания этого текста нетрудно приобрести диск на несколько сот гигабайт стоимостью в 50$, терабайтные диски также вполне доступны). Дисковод на рисунке обеспечивал скорость вращения 5400 оборотов в секунду со временем доступа в 9 миллисекунд и максимальной скоростью передачи данных в 33 мегабайта за секунду. Время доступа и передачи указаны приблизительно, поскольку важной характеристикой является латентное время – время задержки, связанное с перемещением головки диска к нужной дорожке, после чего получить требуемые данные на дорожке можно много быстрее. Когда разрабатываются программы, в которых предусмотрена интенсивная работа с диском, нужно учитывать это свойство для оптимизации производительности.

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

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

    Портативный компьютер – лэптоп, MIT Media Lab's XO laptop, – введенный в 2007 году как часть проекта "Каждому ребенку – свой лэптоп" (OLPC project – One Laptop Per Child), был одним из первых компьютеров, спроектированных для широкого распространения в мире, и использовал флеш-память, а не диски.

    (рис 1.6) Лэптор OLPC работающий с EiffelStudio

    Следуя традициям бумажных лент, упомянутых ранее, некоторые устройства памяти являются съемными. Среди наиболее популярных являются USB-устройства, называемые так потому, что они связаны со стандартизованной последовательной шиной для передачи данных (Universal Serial Bus). Они используют технологию флеш-памяти емкостью от 2-х до 16 гигабайт, а теперь и больше. Съемными теперь являются и USB-диски, устроенные так же, как и встроенные диски, но присоединяемые к USB-порту, что и обеспечивает возможность их смены. Емкость таких дисков начинается от 100 GB.

    (рис 1.7) "Флэшка" и USB-диск

    Регистры и иерархия памяти

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

    a := a + b
            

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

    Как результат, базисная иерархия памяти имеет три уровня:

  • регистры, число которых мало, но скорость сравнима со скоростью центрального процессора;
  • первичная память, ядро, емкостью в несколько гигабайт, но более медленная;
  • диск или его эквивалент в несколько сот гигабайт емкостью, но еще более медленный.
  • Типичный порядок времени записи к моменту написания этого текста составлял: 0,5 наносекунды, 50 наносекунд и 5 миллисекунд. Цифры могут со временем изменяться довольно быстро, но порядок отношения обычно остается. Рассмотрим в частности отношение между двумя последними видами памяти, примерно равное 100000. Спроецируем эти отношения на человеческий уровень выполнения работ. Вообразите рабочего, который:

  • способен обработать деталь, если она уже находится в нужном месте, за 0.01 секунды (аналог операций процессора, когда операнды помещены в регистры);
  • получать заготовку из рядом расположенного хранилища, тратя на это одну секунду (аналог главной памяти, большой, но все же ограниченной);
  • получать неограниченное количество заготовок от поставщика, удаленного за сотни километров, так что на доставку уходят целые сутки.
  • Для компьютера все цифры нужно делить на 20 000 000, но соотношение остается тем же самым. Политика управления памятью – что хранить в оперативной памяти, что на диске – существенно влияет на производительность.

    Виртуальная память

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

    Виртуальная память предоставляет программисту адресное пространство, значительно превосходящее физическое адресное пространство. Система управления памятью, стоящая за сценой, обеспечивает подкачку нужных данных с диска, когда они требуются, но не находятся в первичной памяти. Технически вся память разделяется на единицы, называемые страницами, каждая обычно размером в несколько килобайт. Фактический доступ к данным требует, чтобы они были в ядре, если это не так, то возникает ошибка доступа к странице, система виртуальной памяти в этом случае загружает соответствующую страницу из диска – эта операция называется "загрузка страницы" (page in). При загрузке страницы может возникнуть ситуация, когда физическая память использована и для страницы уже нет места. В этом случае предварительно произойдет "выгрузка страницы" на диск (page out). Есть разные сценарии, позволяющие выгружать страницы, которые с большой долей вероятности не понадобятся в ближайшее время.

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

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

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

    1.3. Команды компьютера

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

    Типичная команда компьютера хранится в слове памяти, иногда в нескольких словах. Она содержит код команды, определяющий ее тип – операцию, выполняемую командой, а также ноль или более требуемых аргументов, которые могут быть адресами или значениями. Для примера рассмотрим команду компьютера с архитектурой 32-битного Power PC. Команда занимает слово (32 бита) с нумерацией битов, начинающейся с нуля:

    Код команды является комбинацией первичного кода 31 (двоичное 11111), заданного битами от 0 до 5, и вторичного кода 266 (двоичное 100001010) в битах от 22 до 31. Результат помещается в регистр 5 (двоичный код 101), а операнды читаются из регистров 3 и 4.

    Побитовое представление неудобно для практического использования человеком. Обычная нотация заменяет двоичное представление более коротким – шестнадцатеричным (группируя биты в четверки, которые заменяются одной шестнадцатеричной цифрой) или восьмеричным (биты группируются тройками). Выше представленное слово 01111100101000110010000100001010 равно 7CA3210A в шестнадцатеричной системе, где буквы от A до F являются цифрами со значениями от 10 до 15.

    Компьютеры обладают командами трех разных типов.

  • Вычисления: арифметические операции, такие как сложение (в приведенном примере), вычитание, умножение, деление с вариантами для целых и вещественных чисел, операции сравнения, побитовые операции, которые выполняют логические операции над соответствующими парами битов двух слов. Обычно такие операции выполняются над операндами, расположенными в регистрах.
  • Загрузки и хранения: передают данные из оперативной памяти в регистры и обратно, а также обеспечивают передачу данных с другими устройствами аппаратуры.
  • Управления: переключая выполнение к другой точке программного кода в зависимости от выполнения некоторого условия или осуществляя безусловный переход.
  • Каждая команда имеет свой код, такой, как 31 в примере. Более удобно ссылаться на код команды, используя мнемонику. Для Power PC мнемоника команды сложения с кодом 31 – add. Представление программы в машинных командах не является удобной формой для общения с человеком. Язык ассемблера обеспечивает приемлемую, доступную для восприятия человеком форму представления таких программ. На языке ассемблера Power PC рассмотренная нами команда может быть записана так:

    add r5, r3, r4
        

    Язык ассемблера заимствовал у языка программирования возможность применения символических имен: имя регистра, такое как r5, имя команды, такое как add, – так же как использование идентификаторов для адресов и констант. Ответственность за трансляцию ассемблерного кода в машинный код ложится на программную систему, называемую ассемблером, которая представляет простейший вид компилятора. Трансляция облегчается тем, что для языка ассемблера выполняется соответствие "один в один" – одна команда ассемблера, как правило, транслируется в одну машинную команду компьютера. Операторы языка программирования задают куда более высокий уровень абстракции. Еще одна особенность ассемблера состоит в том, что для каждой архитектуры компьютера есть свой язык ассемблера, связанный с этой архитектурой. Современные языки программирования являются переносимыми (независимыми от платформы). Не будучи в полном смысле этого термина языком программирования, язык ассемблера позволяет преодолеть наиболее утомительные моменты, связанные с написанием и чтением программ в машинном коде.

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

    1.4. Закон Мура и эволюция компьютеров

    При рассмотрении влияния производительности аппаратуры на программирование нельзя не учитывать ось времени. Чрезвычайные успехи информационных технологий идут рука об руку с прогрессом в разработке аппаратуры. В 1965 году появилась статья Гордона Е. Мура (Gordon Moore), сооснователя корпорации Intel, в которой он в чрезвычайно яркой форме описал этот феномен. Наибольшую известность получил так называемый "закон Мура", формулируемый следующим образом: "Число компонентов, размещаемых на интегральной схеме при сохранении постоянной стоимости, удваивается каждые 18 месяцев" (заметим, что сам Мур говорил о двух годах, но потом эта константа была уменьшена по результатам наблюдений до полутора лет). Есть несколько вариантов этого закона, но все они говорят об экспоненциальном росте. Эти утверждения не являются в полном смысле этого слова "законами", такими, как законы, открытые Максвеллом, Ньютоном или Эйнштейном. Это наблюдения о прогрессе индустрии в течение нескольких десятилетий – наблюдения, которые оказались пророческими и до сих пор продолжают быть применимыми. Удивительно, что нет никакой другой области человеческой деятельности, хоть сколько-либо напоминающей столь удивительную скорость роста. Автомобили сегодня не в тысячу раз быстрее, чем 20 лет назад.

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

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

    1.5. Дальнейшее чтение

    Стандарт IEEE для арифметики с плавающей точкой, доступный по адресу: ieeexplore.ieee.org/xpl/freeabs_all.jsp?arnumber=4610935

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

    John Markoff: .

    Не являясь научной публикацией, эта статья дает ясное описание необходимости параллельной архитектуры для поддержания закона Мура. Описывает трудности параллельного программирования. В течение многих лет Джон Марков был корреспондентом газеты "Нью-Йорк Таймс" в Силиконовой долине и играл важную роль в индустрии.

    John L. Hennessy and David Patterson; Computer Architecture, Fourth Edition: A Quantitative Approach, Morgan Kauffmann, 2006.

    Классический учебник по архитектуре компьютеров. Дополнения в последнем издании отражают последние тенденции, в частности, переход к параллельной архитектуре. (рис 1.8) Дэвид Паттерсон (2007)

    1.6. Ключевые концепции, изученные в этой лекции

  • Внутреннее представление данных в компьютере использует двоичную систему.
  • Базисной единицей данных является бит с двумя возможными значениями: 0 и 1. Биты группируются в байты, содержащие 8 битов, и слова, обычно из 8-ми или 4-х байтов (64-битная или 32-битная архитектура). Адреса измеряются в байтах. Целые и вещественные числа обычно хранятся в словах. Существуют также более компактные представления из одного или двух байтов. Символы представлены одним байтом (расширенный ASCII) или двумя байтами (Unicode).
  • Измерение величин, отличных от единиц памяти, например скорости, всегда использует десятичные единицы, такие как кило (тысяча), мега (миллион), гига (миллиард), тера ($$10^{12}$$), пета ($$10^{15}$$). Для описания размеров памяти и адресов общей практикой, несмотря на официальные стандарты, принято использовать те же префиксы (кило и другие) для степеней двойки, начиная с $$2^{10} = 1024$$ и примерно равное $$10^3 = 1000$$.
  • Представление целых в компьютере является точным, но только в конечном интервале.
  • Представление вещественных чисел является приближенным. Арифметические операции являются обычно источником появления погрешностей. Реализация численных алгоритмов должна избегать накопления таких ошибок.
  • Иерархия памяти включает регистры, оперативную память и устройства постоянной памяти, такие как диски и флеш-память. Операции процессора применимы к операндам, хранимым в регистрах. Доступ к регистрам – самый быстрый (менее наносекунды), но число регистров невелико. Оперативная память на сегодняшний день имеет порядок нескольких гигабайт со временем доступа примерно в 100 раз более медленным, чем доступ к регистрам. Эта память при отключении от источника питания не сохраняет значения данных. Внешняя память – диски, флеш – в сто тысяч раз медленнее оперативной памяти, но существенно превосходит ее по объему, от сотен гигабайт до терабайтов, обеспечивая сохранность хранимых в ней данных.
  • Машинный код представляет систему команд компьютера – операции нижнего уровня, непосредственно выполняемые компьютером, – вычисления над операндами в регистрах, обмен данными между разными уровнями памяти, передачи управления командам.
  • Для компьютерной индустрии характерен экспоненциальный рост, известный как закон Мура. Поддержание этой тенденции потребовало перехода к многоядерным процессорам и параллельному программированию.
  • Новый словарь

    Address Адрес Bit Бит
    Byte Байт Core Ядро (Первичная память)
    Disk Диск Flash memory Флеш-память
    Giga Гига Hexadecimal Шестнадцатеричный
    Kilo Кило Mega Мега
    Moore's law Закон Мура Multicore (and manycore) Многоядерный
    Octal Восьмеричный Persistent Живучий (сохраняемый)
    RAM RAM-память прямого доступа Read Чтение
    Register Регистр Removable memory Сменная память
    Primary memory Первичная (оперативная) память Secondary memory Вторичная память
    Storage Хранилище Transient Кратковременный
    Word Слово Write Запись

    1.7. Упражнения

    1.7.1. Словарь

    Дайте точные определения терминам словаря.

    1.7.2. Карта концепций

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

    1.7.3 Измерения

    Сколько байтов в:

  • килобайте
  • мегабайте
  • мегаслове (слово = 4 байта)
  • гигабайте?
  • 1.7.4. Ваш новый лэптоп

    Каталог рекламирует лэптоп с 1,3 GB памяти.

  • Сколько байтов содержит эта память?
  • Сколько битов содержит эта память?
  • Предположим, что вся память используется для представления одной переменной. Сколько возможных значений может иметь эта переменная? Не требуется выписать точное число (подсказка: не пытайтесь это сделать, если только вы не являетесь владельцем бумажной фабрики; приведите аппроксимацию в форме $$10^n$$).
  • Если бы вы захотели написать это число на бумаге, 100 цифр в строке и 60 строк на странице, то сколько страниц вам бы потребовалось?
  • 1.7.5. Размер и скорость передачи

    Необходимо передать 128 MB данных, используя 128 Mb модем, работающий с максимальной скоростью. Сколько секунд это займет?

    1.7.6. Восьмеричная арифметика

    Восьмеричная арифметика использует систему с основанием 8 и цифрами от 0 до 7.

  • Запишите десятичное число 300000 в восьмеричной системе.
  • Число 74223 в восьмеричной системе запишите как десятичное число.
  • Вычислите сумму этих двух чисел, используя восьмеричную арифметику. Представьте результат в обеих системах.
  • 1.7.7. Шестнадцатеричная арифметика

    Шестнадцатеричная арифметика использует систему с основанием 16 и цифрами от 0 до 9 и A до F.

  • Запишите десятичное число 300000 в шестнадцатеричной системе.
  • Число A42D3 в шестнадцатеричной системе запишите как десятичное число.
  • Вычислите сумму этих двух чисел, используя шестнадцатеричную арифметику. Представьте результат в обеих системах.
  • Вернуться к учебному плану