Почему компилятор C # не оптимизирует ненужные битовые операции? [Дубликат]

И если вы пришли сюда для поиска двух диапазонов столбцов и объединения их друг с другом (например, я), вы можете сделать что-то вроде

op = df[list(df.columns[0:899]) + list(df.columns[3593:])]
print op

. Это создаст новый DataFrame с первым 900 столбцами и ( все) столбцы> 3593 (при условии, что в вашем наборе данных имеется около 4000 столбцов).

26
задан Jon Seigel 29 January 2010 в 15:41
поделиться

6 ответов

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

Тем не менее, другие ответы, которые говорят, что «компилятор» не делает эту оптимизацию, потому что свойство может иметь побочные эффекты как правильные, так и неправильные. Проблема с вопросом (помимо фундаментальной проблемы, на которую просто невозможно ответить без фактической попытки и измерения результата) заключается в том, что «компилятор» на самом деле является двумя компиляторами: компилятор C #, который компилируется в MSIL и JIT-компилятор , который компилирует IL для машинного кода.

Компилятор C # никогда не делает такого рода оптимизацию; как отмечалось, для этого потребуется, чтобы компилятор подключался к вызываемому коду и проверял, что результат, который он вычисляет, не изменяется в течение всего срока действия кода вызываемого абонента. Компилятор C # этого не делает.

Компилятор JIT может. Нет причин, почему это не могло. Здесь есть весь код. Вполне возможно встроить свойство getter, и если джиттер определяет, что встроенное свойство getter возвращает значение, которое может быть кэшировано в регистре и повторно использовано, тогда оно имеет на это право. (Если вы этого не хотите, потому что значение может быть изменено в другом потоке, тогда у вас уже есть ошибка состояния гонки, исправьте ошибку, прежде чем беспокоиться о производительности.)

Действительно ли джиттер делает встроенную функцию fetch и затем регистрирует значение, я понятия не имею. Я почти ничего не знаю о дрожании. Но это разрешено делать, если сочтет нужным. Если вам интересно, делает оно это или нет, вы можете (1) спросить кого-то, кто находится в команде, которая написала джиттер, или (2) проверить jitted-код в отладчике.

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

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

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

58
ответ дан Eric Lippert 21 August 2018 в 08:54
поделиться
  • 1
    +1 Я надеялся обратить ваше внимание на этот вопрос. Спасибо за отличный ответ. – Jon Seigel 29 January 2010 в 17:05
  • 2
    Большой +1 для указания, что «компилятор» на самом деле не является значимым термином в .Net (или Java, если на то пошло, или в любой другой среде, которая компилируется в промежуточный формат и затем применяется [может применяться] JIT). – T.J. Crowder 29 January 2010 в 17:28
  • 3
    @Eric: Ваши ответы всегда замечательные. Даже не видя, как вы называете это в ответ, я знал, что это должно быть вы. Благодаря обмену деталями внутренней работы .Net и C # все мы, смертные. – Matthew Whited 29 January 2010 в 19:14

Если criteria - тип класса, я сомневаюсь, что он будет оптимизирован, потому что другой поток всегда может изменить это значение в то же время. Для struct s я не уверен, но мое чувство кишки состоит в том, что он не будет оптимизирован, но я думаю, что это не повлияет на производительность в этом случае.

0
ответ дан herzmeister 21 August 2018 в 08:54
поделиться
  • 1
    Нет никакой причины, чтобы структура не могла быть изменена в другом потоке почти так же, как класс может. структуры не гарантируются неизменными, и они могут передаваться по ссылке. – Thorarin 29 January 2010 в 15:58
  • 2
    Это очень верно. Смещаемые структуры настолько злы, я никогда их не использую. Таким образом, я почти забыл об этом случае. Итак, мое чувство кишки наконец становится настоящей мудростью. ;-) – herzmeister 29 January 2010 в 16:14

почему бы не протестировать его?

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

На самом деле вы, вероятно, получите около 5 ответов, в которых говорится: «Не стоит беспокоиться об оптимизации». они явно не записывают подпрограммы, которые должны быть как можно быстрее, прежде чем читать (например, игры).

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

На самом деле ТОЛЬКО способ ответить на любой из этих вопросов - это выяснить это это фрагмент кода, который будет полезен при оптимизации. Затем вам нужно знать те вещи, которые увеличивают время выполнения. На самом деле мы, простые смертные, не можем сделать это априори, поэтому просто попробуйте 2-3 разных версии кода, а затем протестируйте их.

2
ответ дан John Nicholas 21 August 2018 в 08:54
поделиться
  • 1
    бум и, конечно же, я ожидал комментария читаемости ... он даже использовал смелый; p – John Nicholas 29 January 2010 в 15:52

Если MaxResults является свойством then no, он не будет его оптимизировать, потому что геттер может иметь сложную логику, например:

private int _maxResults;
public int MaxReuslts {
  get { return _maxResults++; }
  set { _maxResults = value; }
}

Посмотрите, как поведение изменится, если оно будет в строке ваш код?

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

7
ответ дан Nick Craver 21 August 2018 в 08:54
поделиться
  • 1
    Хорошо, даже если бы это было свойство, оно могло быть встроено, но это было бы сделано джиттом. Это значение также может быть изменено другим потоком, поэтому оптимизировать его может быть небезопасно. – Matthew Whited 29 January 2010 в 15:42
  • 2
    @Vinzz: Спасибо! – Nick Craver♦ 29 January 2010 в 15:48

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

Эрик Липперт совершенно правильно указывает, что он сильно зависит от того, что вы подразумеваете под «компилятором». Компилятор C # -> IL? Или компилятор IL -> машинного кода (JIT)? И он прав, чтобы указать, что JIT вполне может оптимизировать свойство getter, так как он имеет всю информацию (тогда как компилятор C # -> IL не обязательно). Это не изменит ситуацию с несколькими потоками, но, тем не менее, это хорошая точка.

6
ответ дан T.J. Crowder 21 August 2018 в 08:54
поделиться

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

Обратите внимание, что фактическая оценка свойства может быть встроена компилятором JIT, что делает его эффективным как быстро как простое поле.

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

4
ответ дан Thorarin 21 August 2018 в 08:54
поделиться
Другие вопросы по тегам:

Похожие вопросы: