A. В императивном языке программирования операторы выполняются последовательно, и каждый оператор может изменять состояние программы. Таким образом, анализ единиц перевода по своей сути является последовательным.
Пример: посмотрите, как может работать постоянное распространение -
a = 5;
b = a + 7;
c = a + b + 9;
Вам необходимо последовательно пройти через эти операторы, прежде чем вы поймете, что значения, присвоенные b
и c
, являются константами во время компиляции.
(Однако отдельные базовые блоки могут быть скомпилированы и оптимизированы параллельно друг с другом.)
B. Кроме того, различные проходы также должны выполняться последовательно и влиять друг на друга.
Пример: на основе расписания инструкций вы распределяете регистры, а затем обнаруживаете, что вам нужно вылить регистр в память, поэтому вам необходимо сгенерировать новые инструкции. Это снова меняет расписание.
Таким образом, вы также не можете выполнять «проходы», такие как «распределение регистров» и «планирование», параллельно (на самом деле, я думаю , есть статьи, в которых ученые-математики / математики пытались решить эти две проблемы вместе, но давайте не будем вдаваться в это).
(Опять же, можно достичь некоторого параллелизма путем конвейерной передачи.)
Более того, графические процессоры особенно не подходят, потому что:
Графические процессоры хороши в математике с плавающей запятой. Что-то компиляторы не нуждаются или не используют много (кроме как при оптимизации арифметики с плавающей точкой в программе)
GPU хороши в SIMD. т. е. выполнение одной и той же операции на нескольких входах. Это опять же, не то, что компилятору нужно делать. может быть полезным, если компилятор должен, скажем, оптимизировать несколько сотен операций с плавающей запятой (дикий пример: программист определил несколько больших массивов FP, назначил им константы, а затем написал код работать над этим. Очень плохо написанная программа действительно.)
Таким образом, кроме распараллеливания компиляции базовых блоков и конвейерного прохода, не так много параллелизма, чтобы имел на уровне «внутри компиляции файла C». Но параллелизм возможен, прост в реализации и постоянно используется на более высоком уровне. Например, GNU Make
имеет аргумент -j=N
. Что в основном означает: пока он находит N
независимых заданий (обычно компиляция группы файлов - это то, для чего GNU Make
используется в любом случае), он порождает N
процессов (или N
экземпляров gcc
компиляции). разные файлы параллельно).