Math.Atan2 и FPATAN

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

Однако я заметил, что эта функция:

static void TemporaryWork()
{
    double x = 4;
    double y = 3;
    double z = Math.Atan2(x, y);
}

Производит эту разборку:

001f0078 55              push    ebp
001f0079 8bec            mov     ebp,esp
001f007b dd05a0001f00    fld     qword ptr ds:[1F00A0h]
001f0081 83ec08          sub     esp,8
001f0084 dd1c24          fstp    qword ptr [esp]
001f0087 dd05a8001f00    fld     qword ptr ds:[1F00A8h]
001f008d 83ec08          sub     esp,8
001f0090 dd1c24          fstp    qword ptr [esp]
001f0093 e86e9ba66f      call    clr!GetHashFromBlob+0x94e09 (6fc59c06) (System.Math.Atan2(Double, Double), mdToken: 06000de7)
001f0098 ddd8            fstp    st(0)
001f009a 5d              pop     ebp
001f009b c3              ret

Даже если вы не гуру x86, вы заметите там кое-что странное: вызов System.Math.Atan2. Как при вызове функции.

Но на самом деле для этого есть код операции x86: FPATAN.

Почему JITer вызывает функцию, когда для выполнения операции есть реальная инструкция по сборке? Я думал, что System.Math по сути является оболочкой для нативных ассемблерных инструкций. Большинство операций там имеют опкоды прямой сборки. Но это, видимо, не так?

Есть ли у кого-нибудь информация о том, почему JITer не может/не может выполнить эту довольно очевидную оптимизацию?


person Jay Lemmon    schedule 21.04.2013    source источник
comment
Если я погуглю, bug in fpatan я получу некоторые результаты для некоторых моделей процессоров, так что это может быть Math.Atan2, работающим над этими ошибками, и заменившим его в Math.Atan. Я не совсем уверен, но, возможно, это может быть направление поиска? Хороший вопрос.   -  person Silvermind    schedule 21.04.2013
comment
Есть ли у кого-нибудь информация о том, почему JITer не может/не может выполнить эту довольно очевидную оптимизацию?» Я не претендую на то, чтобы говорить от имени рассматриваемой JIT, но как человек, реализовавший atan2 для нескольких платформ, вызов инструкции FPATAN был бы очевидной пессимизацией. FPATAN намного медленнее, чем хорошая программная реализация, даже с учетом накладных расходов на вызовы функций. (150-300 циклов против 50-100 циклов, в зависимости от конкретной библиотеки и оборудования).   -  person Stephen Canon    schedule 22.04.2013


Ответы (1)


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

Что приведет вас к clr/src/classlibnative/float/comfloat.cpp, функции ComDouble::Atan2(). Что объясняет причину:

   // the intrinsic for Atan2 does not produce Nan for Atan2(+-inf,+-inf)
   if (IS_DBL_INFINITY(x) && IS_DBL_INFINITY(y)) {
       return(x / y);      // create a NaN
   }
   return (double) atan2(x, y);

Таким образом, это обходной путь для исправления поведения FPU, несовместимого с CLI.

person Hans Passant    schedule 21.04.2013
comment
Я бы не сказал, что здесь что-то ремонтируется. Это адаптер, а не фиксатор. - person David Heffernan; 21.04.2013
comment
Ах, круто, но, следуя источнику, я не думаю, что это обязательно причина. Похоже, что есть много функций, которые не отображаются на встроенные функции, но они не делают ничего лишнего. Например, COMDouble::Log просто вызывает встроенный журнал. Из ecall.cpp только sin, cos, sqrt, round и abs сопоставляются непосредственно с внутренними функциями. - person Jay Lemmon; 21.04.2013
comment
Вы также можете увидеть код для ComDouble::Exp и Log, опять же с комментарием. Он отмечает, что их версия SSE2 слишком медленная, а Exp имеет аналогичную проблему с доменом. Они не используют встроенную функцию, она вызывает CRT-версии этих функций. Созданный вручную ассемблерный код от Intel. Если у вас есть теория лучше, чем довольно явные комментарии, мы будем рады ее услышать. - person Hans Passant; 21.04.2013
comment
Я видел комментарий, но похоже, что он говорит о fpfast и fpprecise. Насколько я знаю, математические функции в math.h являются встроенными, по крайней мере, в том смысле, что компилятор заменяет их кодами операций на всех платформах, на которые я смотрел (что, по общему признанию, не так много) . Насколько я могу судить, математические функции comfloat.cpp (журнал, опыт и т. д.) извлекаются из common.h, который извлекает их из math.h. - person Jay Lemmon; 22.04.2013
comment
Я уже упоминал, что в нескольких местах они не являются встроенными. Просто напишите небольшую тестовую программу на C, чтобы убедиться в этом самостоятельно. - person Hans Passant; 22.04.2013
comment
Вы правы, теперь я еще больше запутался. Зачем все дополнительные накладные расходы, даже в стране C? Почему бы просто не заменить вызовы инструкциями FPU? - person Jay Lemmon; 22.04.2013
comment
@JayLemmon: инструкция fpatan, реализованная микрокодом на современных процессорах, не очень быстрая. Встраивание инструкций x87 не обязательно является лучшим способом реализации библиотеки совпадений, за исключением примитивных операций, таких как fsqrt / fmul / fadd, которые процессоры реализуют непосредственно в аппаратном обеспечении, а не через микрокод. agner.org/optimize. (Или с SSE2 или AVX, используя их вместо устаревшего x87). Если компромисс между точностью и производительностью инструкции x87 соответствует тому, что вы хотите, иногда их стоит использовать. Но не использовать их — очень разумное решение. - person Peter Cordes; 12.10.2019
comment
@PeterCordes - Спасибо за объяснение. В то время у меня сложилось впечатление, что любая инструкция в наборе инструкций x87 будет реализована аппаратно, но, очевидно, это не всегда так. - person Jay Lemmon; 06.12.2019