Почему компиляторы такие глупые? - PullRequest
39 голосов
/ 02 января 2009

Мне всегда интересно, почему компиляторы не могут понять простые вещи, которые очевидны для человеческого глаза. Они делают много простых оптимизаций, но никогда не являются чем-то даже немного сложным. Например, этот код занимает около 6 секунд на моем компьютере, чтобы напечатать нулевое значение (используя Java 1.6):

int x = 0;
for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
    x += x + x + x + x + x;
}
System.out.println(x);

Совершенно очевидно, что x никогда не изменяется, поэтому независимо от того, как часто вы добавляете 0 к себе, оно остается равным нулю. Таким образом, теоретически компилятор может заменить это на System.out.println (0).

Или даже лучше, это занимает 23 секунды:

public int slow() {
    String s = "x";
    for (int i = 0; i < 100000; ++i) {
        s += "x";
    }
    return 10;
}

Сначала компилятор мог заметить, что я на самом деле создаю строку s размером 100000 "x", чтобы он мог вместо этого автоматически использовать s StringBuilder или, что еще лучше, напрямую заменить ее полученной строкой, поскольку она всегда одинакова. Во-вторых, он не распознает, что я вообще не использую строку, поэтому весь цикл может быть отброшен!

Почему, после того, как так много рабочей силы уходит на быстрые компиляторы, они все еще настолько глупы?

РЕДАКТИРОВАТЬ : Конечно, это глупые примеры, которые никогда не должны нигде использоваться. Но всякий раз, когда мне приходится переписывать красивый и очень читаемый код во что-то нечитаемое, чтобы компилятор был доволен и создавал быстрый код, я удивляюсь, почему компиляторы или какой-то другой автоматизированный инструмент не могут выполнить эту работу за меня.

Ответы [ 28 ]

1 голос
/ 01 октября 2010

В режиме выпуска VS 2010 C ++ запуск не занимает много времени. Однако режим отладки - другая история.

#include <stdio.h>
int main()
{
    int x = 0;
    for (int i = 0; i < 100 * 1000 * 1000 * 1000; ++i) {
        x += x + x + x + x + x;
    }
    printf("%d", x);
}
0 голосов
/ 23 ноября 2015

Помещение: я изучал компиляторы в университете.

Компилятор javac чрезвычайно глуп и не выполняет никакой оптимизации, потому что для их выполнения он зависит от среды выполнения java. Среда выполнения поймает эту вещь и оптимизирует ее, но она поймает ее только после того, как функция будет выполнена несколько тысяч раз.

Если вы используете лучший компилятор (например, gcc), включающий оптимизацию, он оптимизирует ваш код, потому что это довольно очевидная оптимизация.

0 голосов
/ 06 ноября 2012

Я не хочу поднимать это на таком старом вопросе (как я сюда попал, в любом случае?), Но я думаю, что часть этого может быть чем-то вроде воздержания со времен Commodore 64.

В начале 1980-х все работало на фиксированных часах. Не было никакого Turbo Boosting, и код всегда создавался для конкретной системы с конкретным процессором и определенной памятью и т. Д. В Commodore BASIC стандартный метод реализации delay s выглядел очень похоже на:

10 FOR X = 1 TO 1000
20 NEXT : REM 1-SECOND DELAY

(На самом деле на практике это больше напоминало 10FORX=1TO1000:NEXT, но вы понимаете, о чем я.)

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

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

Просто порадуйтесь, что ваш код несколько оптимизирован, в отличие от кода на C64. Отображение растрового изображения на C64 может занять до 60 секунд с наиболее эффективными циклами BASIC; таким образом, большинство игр и т. д. были написаны на машинном языке. Написание игр на машинном языке - это не весело.

Только мои мысли.

0 голосов
/ 13 января 2009

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

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

0 голосов
/ 13 января 2009

Поскольку авторы компиляторов пытаются добавить оптимизацию для вещей, которые имеют значение (я надеюсь) и которые измеряются в тестах * Stone (я боюсь).

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

Что меня смущает, так это то, что даже сегодня большинство компиляторов генерируют код для проверки, что значение switchValue больше 255 для плотного или почти полного переключения беззнакового символа. Это добавляет 2 инструкции к внутреннему циклу большинства интерпретаторов байт-кода.

0 голосов
/ 02 января 2009

Это заставляет вас (программиста) думать о том, что вы пишете. Принуждение компиляторов выполнять свою работу за вас никому не помогает: это делает компиляторы намного более сложными (и медленнее!), И делает вас глупее и менее внимательным к вашему коду.

0 голосов
/ 02 января 2009

Это пример процедурного кода против функционального кода.

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

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

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

0 голосов
/ 02 января 2009

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

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

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

Чрезвычайная иная ситуация. У нас есть умный компилятор, компилирующий Windows. Тонны кода для компиляции. Но если он умный, он сводится к 3 строкам кода ...

"starting windows"
"enjoy freecell/solitaire"
"shutting down windows"

Остальная часть кода устарела, потому что он никогда не использовался, не затрагивался и не использовался. Мы действительно этого хотим?

...