Научная статья на тему 'Математическая модель времени вычислений для оптимизации программ'

Математическая модель времени вычислений для оптимизации программ Текст научной статьи по специальности «Математика»

CC BY
150
25
i Надоели баннеры? Вы всегда можете отключить рекламу.
Ключевые слова
КОМПИЛЯТОР / ОПТИМИЗАЦИЯ ПРОГРАММЫ / ПОСЛЕДОВАТЕЛЬНОСТЬ ПРЕОБРАЗОВАНИЙ / РАСПРЕДЕЛЕНИЕ РЕГИСТРОВ / ПОДСТАНОВКА ВПЕРЕД / ПОНИЖЕНИЕ СИЛЫ ОПЕРАЦИЙ / ВЫНОС ИНВАРИАНТОГО ВЫРАЖЕНИЯ / СOMPILER / CODE OPTIMIZATION / PHASE ORDERING / REGISTER ALLOCATION / FORWARD SUBSTITUTION / OPERATOR STRENGTH REDUCTION / INVARIANT CODE MOTION

Аннотация научной статьи по математике, автор научной работы — Макошенко Денис Валентинович

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

i Надоели баннеры? Вы всегда можете отключить рекламу.
iНе можете найти то, что вам нужно? Попробуйте сервис подбора литературы.
i Надоели баннеры? Вы всегда можете отключить рекламу.

The Mathematical Model of Exectution Time for Program Optimization

The paper considers a task of seeking balance between number of operations and register pressure during minimization of program execution time. For this purpose, a model for execution time and a graph describing dependencies between potential transformations are introduced. Introduced apparatus may serve as foundation for algorithms choosing optimal combination of transformations, and utilized in optimizing compilers.

Текст научной работы на тему «Математическая модель времени вычислений для оптимизации программ»

УДК 519.6 + 681.3.012

МАТЕМАТИЧЕКАЯ МОДЕЛЬ ВРЕМЕНИ ВЫЧИСЛЕНИЙ ДЛЯ ОПТИМИЗАЦИИ ПРОГРАММ

© 2009 г. Д.В. Макошенко

Южный федеральный университет, Southern Federal University,

344090, Ростов н/Д, ул. Мильчакова, 8а, 344090, Rostov-on-Don, Milchakov St., 8a,

dnjme@math. sfedu.ru dnjme@math. sfedu.ru

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

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

The paper considers a task of seeking balance between number of operations and register pressure during minimization of program execution time. For this purpose, a model for execution time and a graph describing dependencies between potential transformations are introduced. Introduced apparatus may serve as foundation for algorithms choosing optimal combination of transformations, and utilized in optimizing compilers.

Keywords: сompiler, code optimization, phase ordering, register allocation, forward substitution, operator strength reduction, invariant code motion.

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

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

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

В современных архитектурах стоимость операций с памятью очень велика по сравнению с операциями, операнды которых лежат на регистрах. Для примера в современных процессорах Core Duo из семейства архитектур x86, в зависимости от того, где в иерархии памяти находятся данные, операция загрузки в регистр могут стоить: 1 ^ 3 такта процессора, если дан-

ное находится в КЭШе первого уровня, 10^15 - 2-го, 200^300, если данное не присутствует в КЭШе.

В процессоре Core Duo целочисленное сложение занимает один такт процессора, умножение - 10, время выполнения операций с плавающей точкой лежит в пределах 4^6 тактов (исключая деление) [2]. Это соотношение между арифметическими операциями и операциями с памятью приблизительно сохраняется и для других современных архитектур.

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

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

Проблема выбора комбинации преобразований

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

Пример.

a = q; (0)

b = w; (1)

m = o + e + f; (2)

d = e + f; (3)

for (i=0; i<n; i+=2) { (4)

k = doo(i); (5)

x[i] = a+ b * i; (6)

y[i] = a + b; (7)

z[i] = d; (8)

x[i + 1] = foo(e) * h / k; (9)

y[i + 1] = bar(f); (10)

z[i + 1] = h / k + m; (11) }

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

Вынос инвариантного выражения а + Ь в строке (7) сокращает количество операций, но увеличивает регистровое давление на 1. Если будет проведено последнее нерассмотренное еще преобразование - понижение силы операций, ситуация может измениться.

Понижение силы операций, проведенное в строке (6) к выражению а + Ь * ^ будучи применено без выноса инвариантного выражения а + Ь, уменьшает количество операций на 2, но увеличивает регистровое давление на 1. Если же инвариантное выражение было вынесено, то увеличения регистрового давления не происходит.

В примере оптимальной комбинацией является подстановка вперед выражения е + £ вынос инвариантного выражения а + Ь за цикл и понижение силы операций, примененное к а + Ь * £ в предположении, что регистровое давление велико. В результирующем фрагменте 1 операция умножения удалена из цикла, давление на регистры уменьшилось на 1.

Необходимые понятия

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

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

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

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

Время жизни переменной - множество вершин графа потока управления. Из каждой из них существует путь, по которому можно попасть к вершине, содержащей использование этой переменной до ее переопределения.

Модель времени вычислений

Две программы (процедуры) называются эквивалентными, если для любого допустимого набора входных данных их выходные данные совпадают [3]. Аналогично можно определить эквивалентность циклических участков программы.

МВВ - это функция, которая дает некоторую оценку времени выполнения оптимизируемого участка.

Множество графов потока управления эквивалентных циклических участков - это и есть область определения МВВ. МВВ = Топераций + ТсброСов, где Топерацш - время выполнения операций в предположении бесконечного количества регистров; Тсбросов -время выполнения сбросов.

Линейные участки В!, B2, ... , Вп - вершины графа потока управления.

Топераций Б1 =1,п

^В^, где t - функция, описывающая время исполнения линейного участка. Пусть М(В1) - количество исполнений линейного участка во время выполнения программы, N^0 вычисляется путем статического или динамического профилирования [4]. ^Вп) = (Б =1,п Ыепсу(ОО) * N(61), где Мепеу(О0 - время исполнения операции О1.

МВВ дает лишь аппроксимацию времени исполнения программ ввиду погрешности профиля программы и различного времени исполнения одних и тех же операций в зависимости от контекста [2].

Назначение регистров с помощью графовой модели

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

Затем можно построить неориентированный граф, используемый при назначении переменных на физические регистры [5]. Вершины графа - переменные циклического участка; ребро соединяет вершины, если время жизни соответствующих переменных пересекается.

Алгоритм назначения переменных на регистры основан на алгоритме раскраски графа [6]. Каждому цвету в раскраске графа соответствует физический регистр. При нехватке цветов выбирается переменная, которая сбрасывается в память, т.е. не будет все время лежать на регистре. Перед каждым использованием она читается из памяти, при каждом определении записывается в память. Сброс переменной в память эквивалентен введению новых переменных. Объединение времени их жизни есть подмножество времени жизни исходной переменной, пересечение - пусто. После сброса граф корректируется, чтобы отобразить изменения, связанные с исчезновением сбрасываемой переменной и появлением новых. Затем процесс раскраски повторяется снова.

Заметим, что выбранный метод сбросов [5] понижает максимальную степень вершин графа не более чем за MaxReg + 1 шагов, где MaxReg - максимальное количество регистровых операндов у одной инструкции в конкретной машинной архитектуре. Например, в архитектуре x86 MaxReg=3, в Itanium MaxReg=4. Граф всегда может быть раскрашен количеством цветов, большим на 1, чем максимальная степень его вершин.

Классификация переменных в оптимизируемом участке

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

По определению заголовок циклического участка обладает тем свойством, что через него проходят все циклические пути участка. Переменная является живой во всем циклическом участке, если она жива вдоль хотя бы одного циклического пути внутри него. Таким образом, LLRV (Long live region variables) - множество переменных, живых во всем циклическом участке. Оно совпадает с множеством переменных, живых на входе в заголовочный участок цикла.

Пусть SLRV (Short live region variables) - множество переменных, которые живы не во всем циклическом участке.

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

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

Будем рассматривать только подстановки выражений извне циклического участка в циклический. Проведение такого преобразования всегда будет увеличивать Топераций (при условии, что тело циклического участка исполняется больше одного раза). Однако, если переменные из подставляемого выражения также

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

Заметим, что при проведении подстановки вперед не ухудшается регистровое давление за пределами циклического участка.

Вычисление времени сбросов в память

Определим вес вершины графа назначения регистров: weight = (X =i,n usei) * reload + (X =i,n defi) * save, где usei - число исполнений линейного участка, в котором находится использование переменной, соответствующей i-й вершине; defi - число исполнений линейного участка, в котором находится определение переменной, соответствующей i-й вершине; reload -время чтения из памяти; save - время записи в память.

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

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

Посчитаем Тсбросов для циклического участка. Пусть Reg(LLRV) - число регистров, необходимых для размещения множества LLRV. Величина Reg(LLRV) равна мощности множества LLRV. Число регистров Reg(SLRV), необходимых для размещения SLRV, известно из начального распределения, и очевидно, удовлетворяет неравенству Reg(SLRV) < R. Поэтому условием для появления сбросов переменных из LLRV является выполнение неравенства Reg(LLRV) + Reg(SLRV) > R. Пусть LowerBound = = Reg(LLRV) + Reg(SLRV) - R. Тогда искомое количество сбросов NS удовлетворяет неравенству LowerBound < NS < Min (LBound*MaxReg + 1, |LLRV| + + |SLRV|). Пусть SV - подмножество множества всех переменных цикла, содержащее NS переменных, обладающих минимальным весом. Тогда Тсбросов = = X v е sv weight(v).

Граф зависимостей возможных преобразований (ГЗВП)

Рассматриваемый набор преобразований содержит пару взаимно обратных: подстановка вперед (в тело цикла) и вынос инвариантного выражения за цикл. Используя этот факт, на подготовительном шаге оптимизации цикла можно провести все возможные подстановки вперед, и в дальнейшем строить граф только для выноса инвариантного выражения и понижения силы операций.

Введем понятие несовместимых преобразований. Рассмотрим понижение силы операций для выраже-

ний invariant * inductive и invariant * (inductive + 1). Можно применить понижение силы операций к этим выражениям двумя способами. В первом случае для каждого выражения вводится новая индуктивная переменная. Во втором можно выразить одну новую индуктивную переменную через другую, поскольку на каждой итерации значение одной отличается на значение «invariant» от другой. Очевидно, одновременное применение преобразований, соответствующих первому и второму способу, некорректно. Такие преобразования будем называть несовместимыми.

Множество вершин ГЗВП распадается на два подмножества. Первое состоит из вершин, соответствующих вхождениям исходных переменных и констант. Второе подмножество вершин - преобразования.

Множество ребер графа ГЗВП также разбивается на два подмножества. Ребра из первого соединяют вершины 1-го типа с потенциальными преобразованиями, в которых они участвуют. Такие ребра соединяют два преобразования, если одно из них зависит от другого. Ребра из второго подмножества соединяют два преобразования, которые являются несовместимыми.

Поступила в редакцию

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

Разработанную методику предполагается реализовать в открытой распараллеливающей системе [7].

Литература

1. Muchnik St. Advanced compiler design and implementation. San Francisco, USA, 1997.

2. Intel® 64 and IA-32 Architectures Optimization Reference Manual. URL: http://www.intel.com/products/processor/manuals/ (дата обращения: 03.09.2007).

3. Штейнберг Б.Я. Математические методы распараллеливания рекуррентных циклов для суперкомпьютеров с параллельной памятью. Ростов н/Д, 2004. С. 15.

4. Wu Y., Larus J.R. Static Branch Frequency and Program Profile Analysis // IEEE/ACM Inter'l Symposium on Microarchitecture, 19. San Jose, USA, 1994.

5. Briggs P. Register allocation via graph coloring. A thesis submitted in partial fulfillment of the requirement for the Degree Doctor of Philosophy. Houston, Texas, 1992. 154 p.

6. Ахо А.В., Хопкрофт Д.Э., Ульман Д.Д. Структуры данных и алгоритмы. М., 2000. 384 с.

7. URL: http://www.ops.rsu.ru (дата обращения 10.10.2007).

22 февраля 2008 г.

i Надоели баннеры? Вы всегда можете отключить рекламу.