Почему нет предупреждения, как C4738 для двойной? - PullRequest
7 голосов
/ 26 января 2012

Visual C ++ может выдавать C4738 предупреждение :

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

для случаев, когда 32-битный float собирается быть сохраненным в памяти, а не в регистре.

В описании также сказано, что использование double решает проблему. Я не понимаю, почему последнее верно.

Почему сохранение float в памяти приводит к потере производительности, а сохранение double - нет?

Ответы [ 2 ]

8 голосов
/ 26 января 2012

Предупреждение объединяет две проблемы:

  • Плавания должны храниться в памяти, а не в регистрах, что может снизить производительность (поскольку память намного медленнее, чем регистры)
  • Плавания будутбыть округленным (поскольку регистры всегда имеют 64 или 80 битов, но в памяти число с плавающей запятой имеет только 32 бита).

Использование двойного решает вторую проблему (по крайней мере частично, 64 бита все еще менее точны80 бит), но никак не влияет на возможную потерю производительности.Вот почему в описании предупреждения упоминаются ДВА средства:

Чтобы устранить это предупреждение и избежать округления, скомпилируйте / fp: fast или используйте doubles вместо float.

Чтобы устранить это предупреждениеи избегайте исчерпания регистров, измените порядок вычислений и измените использование inlining

3 голосов
/ 26 января 2012

Хотя я не уверен на 100% в причине, вот мое предположение.

Когда компиляция на x86 и SSE2 не включена, компилятор должен использовать стек FP x87 для всех регистров с плавающей запятой.В MSVC в режиме FP по умолчанию установлено округление с точностью до 53 бит.(Я думаю. Я не уверен на 100% в этом.)

Поэтому все операции, выполняемые в стеке FP, выполняются с двойной точностью.

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


Давайте рассмотрим пример на C4738Страница предупреждения , на которую вы ссылались:

float func(float f)
{
    return f;
}

int main()
{
    extern float f, f1, f2;
    double d = 0.0;

    f1 = func(d);
    f2 = (float) d;
    f = f1 + f2;   // C4738
    printf_s("%f\n", f);
}

Когда вы звоните func(), d, вероятно, сохраняется в регистре x87.Однако вызов func() требует, чтобы точность была снижена до одинарной точности.Это приведет к округлению d / сохранению в памяти.Затем перезагружается и повторно повышается до двойной точности в строке f = f1 + f2;.

Однако, если вы используете double весь путь, компилятор может хранить d в регистре - таким образом, обходя издержкииду в и из памяти.


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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...