c++ visual-c++ intrinsics memset demoscene

Cómo usar las funciones intrínsecas de VC++ sin biblioteca en tiempo de ejecución



visual-c++ intrinsics (6)

Estoy involucrado en uno de esos desafíos donde intentas producir el binario más pequeño posible, así que estoy construyendo mi programa sin las bibliotecas de tiempo de ejecución de C o C ++ (RTL). No enlace a la versión DLL o la versión estática. Ni siquiera #include los archivos de encabezado. Tengo esto funcionando bien.

Algunas funciones de RTL, como memset() , pueden ser útiles, así que intenté agregar mi propia implementación. Funciona bien en compilaciones de depuración (incluso para aquellos lugares donde el compilador genera una llamada implícita a memset() ). Pero en las compilaciones de Release, aparece un error que dice que no puedo definir una función intrínseca. Verá, en las compilaciones de Release, las funciones intrínsecas están habilitadas y memset() es intrínseco.

Me encantaría usar el intrínseco de memset() en mis compilaciones de lanzamiento, ya que probablemente esté en línea y sea más pequeño y más rápido que mi implementación. Pero parece que estoy en catch-22. Si no defino memset() , el enlazador se queja de que no está definido. Si lo defino, el compilador se queja de que no puedo definir una función intrínseca.

¿Alguien sabe la combinación correcta de definición, declaración, #pragma y banderas del compilador y enlazador para obtener una función intrínseca sin tener que recurrir a la sobrecarga de RTL?

Visual Studio 2008, x86, Windows XP +.

Para hacer el problema un poco más concreto:

extern "C" void * __cdecl memset(void *, int, size_t); #ifdef IMPLEMENT_MEMSET void * __cdecl memset(void *pTarget, int value, size_t cbTarget) { char *p = reinterpret_cast<char *>(pTarget); while (cbTarget > 0) { *p++ = static_cast<char>(value); --cbTarget; } return pTarget; } #endif struct MyStruct { int foo[10]; int bar; }; int main() { MyStruct blah; memset(&blah, 0, sizeof(blah)); return blah.bar; }

Y construyo así:

cl /c /W4 /WX /GL /Ob2 /Oi /Oy /Gs- /GF /Gy intrinsic.cpp link /SUBSYSTEM:CONSOLE /LTCG /DEBUG /NODEFAULTLIB /ENTRY:main intrinsic.obj

Si compilo con mi implementación de memset() , obtengo un error de compilación:

error C2169: ''memset'' : intrinsic function, cannot be defined

Si compilo esto sin mi implementación de memset() , memset() un error del enlazador:

error LNK2001: unresolved external symbol _memset


  1. Estoy bastante seguro de que hay una bandera del compilador que le dice a VC ++ que no use intrínsecos

  2. El origen de la biblioteca de tiempo de ejecución se instala con el compilador. Usted tiene la opción de seleccionar las funciones que desea / necesita, aunque a menudo tendrá que modificarlas extensamente (porque incluyen características y / o dependencias que no quiere / necesita).

  3. También hay otras bibliotecas de tiempo de ejecución de código abierto disponibles, que pueden necesitar menos personalización.

  4. Si realmente lo dice en serio, necesitará saber (y tal vez usar) lenguaje ensamblador.

Editado para agregar:

Tengo su nuevo código de prueba para compilar y vincular. Estas son las configuraciones relevantes:

Enable Intrinsic Functions: No Whole Program Optimization: No

Es el último que suprime los "ayudantes de compilación" como el memset incorporado.

Editado para agregar:

Ahora que está desacoplado, puede copiar el código de asm de memset.asm en su programa; tiene una referencia global, pero puede eliminarlo. Es lo suficientemente grande como para que no esté en línea, aunque si elimina todos los trucos que utiliza para ganar velocidad, es posible que sea lo suficientemente pequeño para eso.

Tomé el ejemplo anterior y reemplacé el memset() con esto:

void * __cdecl memset(void *pTarget, char value, size_t cbTarget) { _asm { push ecx push edi mov al, value mov ecx, cbTarget mov edi, pTarget rep stosb pop edi pop ecx } return pTarget; }

Funciona, pero la versión de la biblioteca es mucho más rápida.


Creo que debe establecer Optimización para "Minimizar tamaño (/ O1)" o "Desactivado (/ Od)" para obtener la configuración de lanzamiento para compilar; al menos esto es lo que hizo el truco para mí con VS 2005. Los intrínsecos están diseñados para la velocidad, por lo que tiene sentido que estén habilitados para los otros niveles de Optimización (Velocidad y Completa).


Creo que finalmente encontré una solución:

Primero, en un archivo de encabezado, declare memset() con un pragma, como ese:

extern "C" void * __cdecl memset(void *, int, size_t); #pragma intrinsic(memset)

Eso permite que su código llame a memset() . En la mayoría de los casos, el compilador alineará la versión intrínseca.

En segundo lugar, en un archivo de implementación separado, proporcione una implementación. El truco para evitar que el compilador se queje de la redefinición de una función intrínseca es usar otro pragma primero. Me gusta esto:

#pragma function(memset) void * __cdecl memset(void *pTarget, int value, size_t cbTarget) { unsigned char *p = static_cast<unsigned char *>(pTarget); while (cbTarget-- > 0) { *p++ = static_cast<unsigned char>(value); } return pTarget; }

Esto proporciona una implementación para aquellos casos en que el optimizador decide no usar la versión intrínseca.

El inconveniente principal es que debe deshabilitar la optimización de todo el programa (/ GL y / LTCG). No estoy seguro por qué. Si alguien encuentra una forma de hacerlo sin desactivar la optimización global, por favor, toque.


Esto definitivamente funciona con VS 2015: agregue la opción de línea de comando / Oi-. Esto funciona porque "No" en las funciones intrínsecas no es un interruptor, no está especificado. / Oi- y todos tus problemas desaparecen (debería funcionar con la optimización del programa completo, pero no he probado esto correctamente).


La forma en que la biblioteca de tiempo de ejecución "normal" hace esto es compilando un archivo de ensamblaje con una definición de memset y vinculándolo a la biblioteca de tiempo de ejecución (Puede encontrar el archivo de ensamblaje en C: / Program Files / Microsoft Visual Studio 10.0 / VC / crt / src / intel / memset.asm). Ese tipo de cosas funciona bien incluso con la optimización de todo el programa.

También tenga en cuenta que el compilador solo usará el memset intrínseco en algunos casos especiales (cuando el tamaño es constante y pequeño). Por lo general, utilizará la función memset proporcionada por usted, por lo que probablemente debería utilizar la función optimizada en memset.asm, a menos que vaya a escribir algo igual de optimizado.


Simplemente nombra la función algo ligeramente diferente.