Как в аппаратном, так и в программном проектировании, есть главный принцип, который предполагает, что скорость выполнения чего-то, что было сделано в миллион раз, гораздо важнее, чем скорость выполнения чего-то, что было сделано один раз. Следствием этого является то, что, если что-то делается миллион раз, время, необходимое для того, чтобы сделать что-то в первый раз, гораздо менее важно, чем время, необходимое для других 999 999. Одна из главных причин того, что компьютеры сегодня намного быстрее, чем 25 лет назад, заключается в том, что дизайнеры сосредоточены на том, чтобы делать повторяющиеся операции быстрее, даже если это может замедлить выполнение одноразовых операций.
В качестве простого примера с аппаратной точки зрения рассмотрим два подхода к проектированию памяти: (1) существует единое хранилище памяти, и каждая операция занимает шестьдесят наносекунд; (2) существует несколько уровней кэша; извлечение слова, которое содержится в первом уровне кэша, займет одну наносекунду; слово, которого там нет, но которое хранится на втором уровне, займет пять; Слово, которого нет, но находится на третьем уровне, займет десять, а слово, которого там нет, займет шестьдесят. Если бы все обращения к памяти были абсолютно случайными, первый дизайн был бы не только проще, чем второй, но и работал бы лучше. Большинство обращений к памяти приводит к тому, что ЦП тратит десять наносекунд на поиск данных в кеше, прежде чем выходить из него и извлекать его из основной памяти. С другой стороны, если 80% обращений к памяти удовлетворяется первым уровнем кэша, 16% - вторым и 3% - третьим, то есть только один из ста должен выходить в основную память, тогда среднее время для этих обращений к памяти будет 2,5 нс. Это в среднем в сорок раз быстрее, чем простая система памяти.
Даже если целая программа предварительно загружена с диска, при первом запуске такой подпрограммы, как «printf», ни она, ни любые требуемые ей данные, скорее всего, не попадут в кэш любого уровня. Следовательно, медленный доступ к памяти потребуется при первом запуске. С другой стороны, после того как код и большая часть его необходимых данных будут кэшированы, будущие исполнения будут намного быстрее. Если повторное выполнение фрагмента кода происходит, пока он все еще находится в самом быстром кеше, разница в скорости может легко составить порядок величины. Оптимизация для быстрого случая во многих случаях приведет к тому, что однократное выполнение кода будет намного медленнее, чем это было бы в противном случае (даже в большей степени, чем предлагается в приведенном выше примере), но поскольку многие процессоры тратят большую часть своего времени на выполнение небольших фрагментов кода в миллионы или миллиарды раз, ускорения, полученные в этих ситуациях, намного перевешивают любое замедление при выполнении подпрограмм, которые выполняются только один раз.