C ++ явное определение шаблона - код все еще дублируется - PullRequest
0 голосов
/ 08 мая 2018

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

Я пытался следовать идеям следующих постов Stackoverflow:

и похожи на дубликаты вопросов. Я придумал следующий тест, чтобы проиллюстрировать проблему:

хиджры

#pragma once
#include <cmath>
template<typename T>
struct A
{
    static T foo(T a, T b)
    {
        //do some heavy computations
        T v1 = pow(a, b);
        return pow(v1, b);
    }
};

//explicit template instantiations, the declaration
extern template struct A<float>;
extern template struct A<double>;

a.cpp

#include "A.h"
//explicit template instantiations, the definition
template struct A<float>;
template struct A<double>;

main.cpp

#include "A.h"
int main()
{
    //use A
    float result = A<float>::foo(0, 0);
    return (int)result; //return it so that it doesn't get optimized away
}

Когда я сейчас смотрю на сгенерированный файл .obj (dumpbin / DISASM), я получаю следующий вывод:

A.obj

Dump of file A.obj

File Type: COFF OBJECT

?foo@?$A@M@@SAMMM@Z (public: static float __cdecl A<float>::foo(float,float)):
  0000000000000000: F3 0F 11 4C 24 10  movss       dword ptr [rsp+10h],xmm1
  0000000000000006: F3 0F 11 44 24 08  movss       dword ptr [rsp+8],xmm0
  000000000000000C: 55                 push        rbp
  000000000000000D: 57                 push        rdi
  000000000000000E: 48 81 EC 18 01 00  sub         rsp,118h
                    00
  0000000000000015: 48 8D 6C 24 30     lea         rbp,[rsp+30h]
  000000000000001A: 48 8B FC           mov         rdi,rsp
  000000000000001D: B9 46 00 00 00     mov         ecx,46h
  0000000000000022: B8 CC CC CC CC     mov         eax,0CCCCCCCCh
  0000000000000027: F3 AB              rep stos    dword ptr [rdi]
  0000000000000029: F3 0F 10 8D 08 01  movss       xmm1,dword ptr [rbp+108h]
                    00 00
  0000000000000031: F3 0F 10 85 00 01  movss       xmm0,dword ptr [rbp+100h]
                    00 00
  0000000000000039: E8 00 00 00 00     call        ?pow@@YAMMM@Z
  000000000000003E: F3 0F 11 45 04     movss       dword ptr [rbp+4],xmm0
  0000000000000043: F3 0F 10 8D 08 01  movss       xmm1,dword ptr [rbp+108h]
                    00 00
  000000000000004B: F3 0F 10 45 04     movss       xmm0,dword ptr [rbp+4]
  0000000000000050: E8 00 00 00 00     call        ?pow@@YAMMM@Z
  0000000000000055: 48 8D A5 E8 00 00  lea         rsp,[rbp+0E8h]
                    00
  000000000000005C: 5F                 pop         rdi
  000000000000005D: 5D                 pop         rbp
  000000000000005E: C3                 ret

?foo@?$A@N@@SANNN@Z (public: static double __cdecl A<double>::foo(double,double)):
  0000000000000000: F2 0F 11 4C 24 10  movsd       mmword ptr [rsp+10h],xmm1
  0000000000000006: F2 0F 11 44 24 08  movsd       mmword ptr [rsp+8],xmm0
  000000000000000C: 55                 push        rbp
  000000000000000D: 57                 push        rdi
  000000000000000E: 48 81 EC 18 01 00  sub         rsp,118h
                    00
  0000000000000015: 48 8D 6C 24 30     lea         rbp,[rsp+30h]
  000000000000001A: 48 8B FC           mov         rdi,rsp
  000000000000001D: B9 46 00 00 00     mov         ecx,46h
  0000000000000022: B8 CC CC CC CC     mov         eax,0CCCCCCCCh
  0000000000000027: F3 AB              rep stos    dword ptr [rdi]
  0000000000000029: F2 0F 10 8D 08 01  movsd       xmm1,mmword ptr [rbp+108h]
                    00 00
  0000000000000031: F2 0F 10 85 00 01  movsd       xmm0,mmword ptr [rbp+100h]
                    00 00
  0000000000000039: E8 00 00 00 00     call        pow
  000000000000003E: F2 0F 11 45 08     movsd       mmword ptr [rbp+8],xmm0
  0000000000000043: F2 0F 10 8D 08 01  movsd       xmm1,mmword ptr [rbp+108h]
                    00 00
  000000000000004B: F2 0F 10 45 08     movsd       xmm0,mmword ptr [rbp+8]
  0000000000000050: E8 00 00 00 00     call        pow
  0000000000000055: 48 8D A5 E8 00 00  lea         rsp,[rbp+0E8h]
                    00
  000000000000005C: 5F                 pop         rdi
  000000000000005D: 5D                 pop         rbp
  000000000000005E: C3                 ret
....

Main.obj

Dump of file Main.obj

File Type: COFF OBJECT

?foo@?$A@M@@SAMMM@Z (public: static float __cdecl A<float>::foo(float,float)):
  0000000000000000: F3 0F 11 4C 24 10  movss       dword ptr [rsp+10h],xmm1
  0000000000000006: F3 0F 11 44 24 08  movss       dword ptr [rsp+8],xmm0
  000000000000000C: 55                 push        rbp
  000000000000000D: 57                 push        rdi
  000000000000000E: 48 81 EC 18 01 00  sub         rsp,118h
                    00
  0000000000000015: 48 8D 6C 24 30     lea         rbp,[rsp+30h]
  000000000000001A: 48 8B FC           mov         rdi,rsp
  000000000000001D: B9 46 00 00 00     mov         ecx,46h
  0000000000000022: B8 CC CC CC CC     mov         eax,0CCCCCCCCh
  0000000000000027: F3 AB              rep stos    dword ptr [rdi]
  0000000000000029: F3 0F 10 8D 08 01  movss       xmm1,dword ptr [rbp+108h]
                    00 00
  0000000000000031: F3 0F 10 85 00 01  movss       xmm0,dword ptr [rbp+100h]
                    00 00
  0000000000000039: E8 00 00 00 00     call        ?pow@@YAMMM@Z
  000000000000003E: F3 0F 11 45 04     movss       dword ptr [rbp+4],xmm0
  0000000000000043: F3 0F 10 8D 08 01  movss       xmm1,dword ptr [rbp+108h]
                    00 00
  000000000000004B: F3 0F 10 45 04     movss       xmm0,dword ptr [rbp+4]
  0000000000000050: E8 00 00 00 00     call        ?pow@@YAMMM@Z
  0000000000000055: 48 8D A5 E8 00 00  lea         rsp,[rbp+0E8h]
                    00
  000000000000005C: 5F                 pop         rdi
  000000000000005D: 5D                 pop         rbp
  000000000000005E: C3                 ret
....

A::foo создается в A.obj, как и ожидалось. Но код снова помещается в Main.obj, полностью игнорируя ключевое слово extern.

Как я могу сказать компилятору (Visual Studio 2017, режим выпуска) НЕ использовать метод inline, а использовать версию из A.obj?

1 Ответ

0 голосов
/ 08 мая 2018

Вы можете сделать это с помощью __ declspec (noinline) .

Но встроенная версия, скорее всего, будет быстрее. Если вас беспокоит размер двоичного файла, в вашем файле .exe будет только один экземпляр этой функции. Код из A.obj не используется и будет удален компоновщиком на этапе устранения мертвого кода.

Обновление: Поместите это в свой A.h:

static __declspec( noinline ) T foo( T a, T b )
{
    //do some heavy computations
    T v1 = pow( a, b );
    return pow( v1, b );
}

Я построил на Visual C ++ 2017 15.6.7, выпуск 32 и 64 бита, для обеих платформ, которые Main.cpp компилирует в это:

; Line 5
    call    ?foo@?$A@M@@SAMMM@Z         ; A<float>::foo
; Line 6
    cvttss2si eax, xmm0

Однако, если вы пытаетесь уменьшить время компиляции, я не уверен, что это поможет. Вместо этого удалите тело функции из A.h (оставьте объявление), переместите его в A.cpp. В идеале также удалите собственные заголовки из A.h (или оставьте минимум, определяющий структуры данных) и включите собственные заголовки в A.cpp.

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