Question

Je suis impliqué dans un de ces défis où vous essayez de produire le plus petit binaire possible, donc je vais construire mon programme sans C ou C ++ bibliothèques d'exécution (RTL). Je ne relions pas à la version DLL ou la version statique. Je ne même pas #include les fichiers d'en-tête. J'ai ce beau travail.

Certaines fonctions de RTL, comme memset(), peut être utile, donc j'ai essayé d'ajouter ma propre mise en œuvre. Il fonctionne très bien dans debug (même pour les lieux où le compilateur génère un implicite appel à memset()). Mais dans les versions Release, je reçois une erreur disant que je ne peux pas définir une fonction intrinsèque. Vous voyez, dans les versions Release, les fonctions intrinsèques sont activées, et memset() est une valeur intrinsèque.

J'aimerais utiliser la valeur intrinsèque pour memset() dans ma libération construit, car il est probablement inline et plus petit et plus rapide que ma mise en œuvre. Mais il me semble être un en catch-22. Si je ne définit pas memset(), l'éditeur de liens se plaint qu'il est défini. Si je le définir, le compilateur se plaint que je ne peux pas définir une fonction intrinsèque.

Quelqu'un sait la bonne combinaison de définition, déclaration, #pragma et drapeaux du compilateur et l'éditeur de liens pour obtenir une fonction intrinsèque sans tirer dans RTL en tête?

Visual Studio 2008, x86, Windows XP +.

Pour le problème un peu plus concret:

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;
}

Et je construis comme ceci:

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

Si je compilez avec ma mise en œuvre de memset(), je reçois une erreur de compilation:

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

Si je compile cela sans ma mise en œuvre de memset(), je reçois une erreur de liens:

error LNK2001: unresolved external symbol _memset
Était-ce utile?

La solution

Je pense que j'ai finalement trouvé une solution:

Tout d'abord, dans un fichier d'en-tête, memset() annoncez avec un pragma, comme suit:

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

qui permet à votre code pour appeler memset(). Dans la plupart des cas, le compilateur inline la version intrinsèque.

En second lieu, dans un fichier de mise en œuvre séparée, fournir une mise en œuvre. L'astuce pour empêcher le compilateur de se plaindre de re-définir une fonction intrinsèque est d'utiliser un autre pragma premier. Comme ceci:

#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;
}

Ceci fournit une mise en œuvre dans les cas où l'optimiseur décide de ne pas utiliser la version intrinsèque.

L'inconvénient est exceptionnel que vous devez désactiver l'optimisation tout le programme (/ GL et / LTCG). Je ne sais pas pourquoi. Si quelqu'un trouve un moyen de le faire sans désactiver l'optimisation globale, s'il vous plaît carillon.

Autres conseils

  1. Je suis sûr qu'il ya un drapeau du compilateur qui indique VC ++ de ne pas intrinsics d'utilisation

  2. La source à la bibliothèque d'exécution est installé avec le compilateur. Vous avez le choix des fonctions que vous voulez / extrayant besoin, bien souvent, vous devrez les modifier en profondeur (parce qu'ils incluent des fonctionnalités et / ou les dépendances que vous ne voulez pas /).

  3. Il existe d'autres bibliothèques d'exécution open source disponibles et qui pourraient avoir besoin moins de personnalisation.

  4. Si vous êtes vraiment sérieux, vous aurez besoin de savoir (et peut-être utiliser) assembleur.

Edité pour ajouter:

Je suis votre nouveau code de test pour compiler et lier. Ce sont les paramètres pertinents:

Enable Intrinsic Functions: No
Whole Program Optimization: No

Il est que la dernière qui réprime « aides du compilateur » comme le haut-memset.

Edité pour ajouter:

Maintenant qu'il est découplé, vous pouvez copier le code asm de memset.asm dans votre programme - il a une référence mondiale, mais vous pouvez supprimer cela. Il est assez grand pour qu'il soit pas inline, mais si vous supprimez toutes les astuces qu'il utilise pour gagner de la vitesse que vous pourriez être en mesure de le faire assez petit pour cela.

Je pris votre exemple ci-dessus et a remplacé le memset() avec ceci:

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;
}

Il fonctionne, mais la version de la bibliothèque est beaucoup plus rapide.

Je pense que vous devez définir l'optimisation de « Réduire la taille (/ O1) » ou « Désactivé (/ Od) » pour obtenir la configuration de sortie pour compiler; au moins c'est ce que fait le truc pour moi avec VS 2005. Intrinsics sont conçus pour la vitesse il est donc logique qu'ils seraient activés pour les autres niveaux d'optimisation (vitesse et plein).

Il suffit de nommer la fonction quelque chose de légèrement différent.

Cela fonctionne certainement avec VS 2015: Ajoutez l'option de ligne de commande / Oi-. Cela fonctionne parce que « Non » sur les fonctions ne sont pas Intrinsic un commutateur, il est non spécifié. / Oi- et tous vos problèmes disparaissent (il devrait fonctionner avec toute l'optimisation des programmes, mais je n'ai pas correctement testé).

La façon dont la bibliothèque d'exécution « régulière » le fait est en compilant un fichier d'assemblage avec une définition de memset et de le relier à la bibliothèque d'exécution (Vous pouvez trouver le fichier de montage dans ou autour de C: \ Program Files \ Microsoft Visual Studio 10.0 \ VC \ crt \ src \ intel \ memset.asm). Ce genre de chose fonctionne très bien même avec l'optimisation tout le programme.

Notez également que le compilateur utilisera uniquement la valeur intrinsèque memset dans certains cas particuliers (lorsque la taille est constante et petite?). Il sera généralement utiliser la fonction memset fournie par vous, alors vous devriez probablement utiliser la fonction optimisée memset.asm, à moins que vous allez écrire quelque chose comme optimisé.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top