Как я могу получить встроенную функцию exp () в коде x64?

У меня есть следующий код, и я ожидаю внутреннюю версиюexp() функция, которая будет использоваться. К сожалению, его нет в сборке x64, что делает его медленнее, чем аналогичный Win32 (то есть 32-разрядная сборка):

<code>#include "stdafx.h"
#include <cmath>
#include <intrin.h>
#include <iostream>

int main()
{
  const int NUM_ITERATIONS=10000000;
  double expNum=0.00001;
  double result=0.0;

  for (double i=0;i<NUM_ITERATIONS;++i)
  {
    result+=exp(expNum); // <-- The code of interest is here
    expNum+=0.00001;
  }

  // To prevent the above from getting optimized out...
  std::cout << result << '\n';
}
</code>

Я использую следующие ключи для моей сборки:

<code>/Zi /nologo /W3 /WX-
/Ox /Ob2 /Oi /Ot /Oy /GL /D "WIN32" /D "NDEBUG" 
/D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm- 
/EHsc /GS /Gy /arch:SSE2 /fp:fast /Zc:wchar_t /Zc:forScope 
/Yu"StdAfx.h" /Fp"x64\Release\exp.pch" /FAcs /Fa"x64\Release\" 
/Fo"x64\Release\" /Fd"x64\Release\vc100.pdb" /Gd /errorReport:queue 
</code>

Как видите, у меня есть/Oi, /O2 а также/fp:fast как требуется согласноСтатья MSDN о внутренностях, Тем не менее, несмотря на мои усилия, сделан вызов стандартной библиотеке, что делаетexp() работать медленнее на сборках x64.

Вот сгенерированная сборка:

<code>  for (double i=0;i<NUM_ITERATIONS;++i)
000000013F911030  movsd      xmm10,mmword ptr [[email protected] (13F912248h)]  
000000013F911039  movapd     xmm8,xmm6  
000000013F91103E  movapd     xmm7,xmm9  
000000013F911043  movaps     xmmword ptr [rsp+20h],xmm11  
000000013F911049  movsd      xmm11,mmword ptr [[email protected] (13F912240h)]  
  {
    result+=exp(expNum);
000000013F911052  movapd     xmm0,xmm7  
000000013F911056  call       exp (13F911A98h) // ***** exp lib call is here *****
000000013F91105B  addsd      xmm8,xmm10  
    expNum+=0.00001;
000000013F911060  addsd      xmm7,xmm9  
000000013F911065  comisd     xmm8,xmm11  
000000013F91106A  addsd      xmm6,xmm0  
000000013F91106E  jb         main+52h (13F911052h)  
  }
</code>

Как вы можете видеть в сборке выше, есть призыв кexp() функция. Теперь давайте посмотрим на код, сгенерированный для этогоfor цикл с 32-битной сборкой:

<code>  for (double i=0;i<NUM_ITERATIONS;++i)
00101031  xorps       xmm1,xmm1  
00101034  rdtsc  
00101036  push        ebx  
00101037  push        esi  
00101038  movsd       mmword ptr [esp+1Ch],xmm0  
0010103E  movsd       xmm0,mmword ptr [[email protected] (102188h)]  
00101046  push        edi  
00101047  mov         ebx,eax  
00101049  mov         dword ptr [esp+3Ch],edx  
0010104D  movsd       mmword ptr [esp+28h],xmm0  
00101053  movsd       mmword ptr [esp+30h],xmm1  
00101059  lea         esp,[esp]  
  {
    result+=exp(expNum);
00101060  call        __libm_sse2_exp (101EC0h) // <--- Quite different from 64-bit
00101065  addsd       xmm0,mmword ptr [esp+20h]  
0010106B  movsd       xmm1,mmword ptr [esp+30h]  
00101071  addsd       xmm1,mmword ptr [[email protected] (102180h)]  
00101079  movsd       xmm2,mmword ptr [[email protected] (102178h)]  
00101081  comisd      xmm2,xmm1  
00101085  movsd       mmword ptr [esp+20h],xmm0  
    expNum+=0.00001;
0010108B  movsd       xmm0,mmword ptr [esp+28h]  
00101091  addsd       xmm0,mmword ptr [[email protected] (102188h)]  
00101099  movsd       mmword ptr [esp+28h],xmm0  
0010109F  movsd       mmword ptr [esp+30h],xmm1  
001010A5  ja          wmain+40h (101060h)  
  }
</code>

Гораздо больше кода, но это быстрее. Временной тест, который я провел на хосте Nehalem-EP с тактовой частотой 3,3 ГГц, дал следующие результаты:

32-бит:

For loop body average exec time: 34.849229 cycles / 10.560373 ns

64-бит:

For loop body average exec time: 45.845323 cycles / 13.892522 ns

Действительно, очень странное поведение. Почему это происходит?

Update:

Я создалОтчет об ошибке Microsoft Connect, Не стесняйтесь высказать свое мнение, чтобы получить авторитетный ответ от самой Microsoft об использовании встроенных функций с плавающей точкой, особенно в коде x64.

 Michael Goldshteyn10 апр. 2012 г., 22:12
@GregC, что касается вашей ссылки на software.intel.com ..., мы не используем библиотеку SVML в наших проектах, так что нет. Я просто пытаюсь привести сборку в соответствие с «гарантиями» Microsoft. на основе MSDN.
 Michael Goldshteyn10 апр. 2012 г., 22:04
Эта статья о 64-битной версии самой Visual Studio, она не имеет ничего общего с поставленным вопросом. Есть много факторов, которые могут сделать 64-битное приложение медленнее, чем 32-битное. Если, конечно, я что-то упускаю, ни один из этих факторов не имеет никакого отношения к моему вопросу о вычислениях с плавающей запятой.
 Michael Goldshteyn10 апр. 2012 г., 22:11
GregC, удаляя / D "WIN32" не влияет на сгенерированный код.
 Attila10 апр. 2012 г., 22:08
@MichaelGoldshteyn - моя ошибка
 Attila10 апр. 2012 г., 22:01
This article (объясняя, почему VS не имеет 64-битной версии), указывает, что 64-битная сборка может быть медленнее, чем 32-битная. Я не знаю, относится ли это объяснение к вашему конкретному случаю.

Ответы на вопрос(3)

Я думаю, что единственная причина, по которой Microsoft предоставляет встроенную версию 32-битного SSE2 exp (), - это стандартные соглашения о вызовах. 32-битные соглашения о вызовах требуют, чтобы операнд был помещен в основной стек, а результат должен быть возвращен в верхний регистр стека FPU. Если у вас включена генерация кода SSE2, то возвращаемое значение, скорее всего, будет извлечено из стека FPU в память, а затем загружено из этого места в регистр SSE2 для любых математических операций, которые вы хотите выполнить с результатом. Очевидно, что быстрее передать операнд в регистр SSE2 и вернуть результат в регистр SSE2. Это то, что делает __libm_sse2_exp (). В 64-битном коде стандартное соглашение о вызовах передает операнд и возвращает результат в регистрах SSE2 в любом случае, поэтому нет никакой необходимости иметь встроенную версию.

Причина различия в производительности между 32-битным SSE2 и 64-битными реализациями exp () заключается в том, что Microsoft использует разные алгоритмы в этих двух реализациях. Я не знаю, почему они это делают, и они дают разные результаты (отличающиеся на 1 целое) для некоторых операндов.

Решение Вопроса

. Это не имеет встроенной операции дляexp() и поэтому вызов стандартной библиотеки неизбежен, если вы не напишите свой собственный встроенный вручную векторизованный__m128d exp(__m128d) (Самая быстрая реализация экспоненциальной функции с использованием SSE).

Я полагаю, что статья MSDN, на которую вы ссылаетесь, была написана с использованием 32-битного кода, использующего 8087 FP.

 Michael Goldshteyn10 апр. 2012 г., 22:48
Удаление SSE2 из 32-битной сборки действительно приводит к совершенно другому коду, который использует 8087 "f" инструкции и я не вижу никакихexp() Lib Call. Однако код работает почти втрое медленнее. Однако кажется, что вы что-то там делаете. Для 64-битных сборок невозможно отключить использование SSE2 в компиляторе, поскольку все 64-битные процессоры должны его поддерживать. Поэтому в сгенерированном (сборочном) коде нет изменений.
 10 апр. 2012 г., 22:43
Могу поспорить, что документация просто не была обновлена с учетом кода SSE. И я подозреваю, что если вы удалите / arch: sse2 из ваших опций и нацелитесь на 8087 FPU, то вы увидите внутренний вызов.
 Michael Goldshteyn10 апр. 2012 г., 22:36
Пожалуйста, смотрите мой отредактированный вопрос, который включает в себя код, сгенерированный 32-битной сборкой, и сравнение синхронизации 32-битной и 64-битной версий. Ни одна из сборок не использует & quot; true & quot; присуща, но есть различия в вызываемой функции, и 32-битная сборка значительно быстрее.
 Michael Goldshteyn10 апр. 2012 г., 22:40
Это правда, но я ожидал, что в соответствии с документацией MSDN внутренняя реализация exp () будет встроена в мой (сборочный) код.
 10 апр. 2012 г., 22:39
Ну, может быть, но факт остается фактом, что ни в одном из опкодов SSE нет опыта exp

Руководства по набору инструкций AMD x64 а такжеСправочник Intel.

При первоначальной проверке должен быть способ использовать F2XM1 для вычисления экспоненты. Однако это в наборе команд x87,скрыто в режиме x64.

Есть надежда на использование MMX / x87 e, явно, как описано в публикации наДоски обсуждений VirtualDub.  А этокак на самом деле написать ASM в VC ++.

 Michael Goldshteyn10 апр. 2012 г., 22:36
Извините, добавляю/MD ничего не изменилось ...

Ваш ответ на вопрос