Как использовать встроенные функции VC ++ без библиотеки времени выполнения - PullRequest
31 голосов
/ 30 мая 2010

Я участвую в одной из тех задач, когда вы пытаетесь создать наименьший возможный двоичный файл, поэтому я создаю свою программу без библиотек времени выполнения C или C ++ (RTL). Я не ссылаюсь на версию DLL или статическую версию. Я даже не #include файлы заголовков. У меня это работает нормально.

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

Я бы хотел использовать встроенную функцию для memset() в моих сборках релиза, поскольку она, вероятно, встроенная, меньше и быстрее, чем моя реализация. Но я, кажется, в улове-22. Если я не определяю memset(), компоновщик жалуется, что он не определен. Если я определю это, компилятор пожалуется, что не могу определить встроенную функцию.

Кто-нибудь знает правильную комбинацию определений, объявлений, #pragma, а также флагов компилятора и компоновщика, чтобы получить встроенную функцию без дополнительных затрат RTL?

Visual Studio 2008, x86, Windows XP +.

Чтобы сделать задачу немного более конкретной:

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

И я строю так:

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

Если я компилирую с моей реализацией memset(), я получаю ошибку компилятора:

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

Если я скомпилирую это без моей реализации memset(), я получу ошибку компоновщика:

error LNK2001: unresolved external symbol _memset

Ответы [ 6 ]

20 голосов
/ 31 мая 2010

Мне кажется, я наконец-то нашел решение:

Сначала в заголовочном файле объявите memset() с прагмой, например:

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

Это позволяет вашему коду звонить memset(). В большинстве случаев компилятор встроит внутреннюю версию.

Во-вторых, в отдельном файле реализации укажите реализацию. Хитрость в том, чтобы не дать компилятору пожаловаться на переопределение встроенной функции, это сначала использовать другую прагму. Как это:

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

Это обеспечивает реализацию для тех случаев, когда оптимизатор решает не использовать встроенную версию.

Выдающимся недостатком является то, что вы должны отключить оптимизацию всей программы (/ GL и / LTCG). Я не уверен почему. Если кто-то найдет способ сделать это, не отключив глобальную оптимизацию, пожалуйста, включите.

5 голосов
/ 30 мая 2010
  1. Я почти уверен, что есть флаг компилятора, который говорит VC ++ не использовать встроенные функции

  2. Исходный код библиотеки времени выполнения устанавливается вместе с компилятором. У вас есть выбор функций, которые вы хотите / нуждаетесь в извлечении, хотя часто вам придется их сильно изменять (потому что они включают функции и / или зависимости, которые вам не нужны / не нужны).

  3. Также доступны другие библиотеки времени выполнения с открытым исходным кодом, которые могут нуждаться в меньшем количестве настроек.

  4. Если вы действительно серьезно относитесь к этому, вам нужно знать (и, возможно, использовать) язык ассемблера.

Отредактировано, чтобы добавить:

Я получил ваш новый тестовый код для компиляции и ссылки. Это соответствующие настройки:

Enable Intrinsic Functions: No
Whole Program Optimization: No

Это последний, который подавляет «помощников компилятора», таких как встроенный memset.

Отредактировано, чтобы добавить:

Теперь, когда он не связан, вы можете скопировать код asm из memset.asm в вашу программу - он имеет одну глобальную ссылку, но вы можете удалить ее. Он достаточно большой, чтобы он был не встроенным, хотя, если вы уберете все трюки, которые он использует для увеличения скорости, вы сможете сделать его достаточно маленьким для этого.

Я взял приведенный выше пример и заменил memset() следующим:

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

Работает, но версия библиотеки намного быстрее.

1 голос
/ 30 мая 2010

Я думаю, вы должны установить Оптимизацию на «Минимизировать размер (/ O1)» или «Отключено (/ Od)», чтобы получить конфигурацию выпуска для компиляции; по крайней мере, это то, что помогло мне с VS 2005. Встроенные функции разработаны для скорости, поэтому имеет смысл включить их для других уровней оптимизации (Speed ​​и Full).

0 голосов
/ 12 февраля 2017

Это определенно работает с VS 2015: Добавьте параметр командной строки / Oi-. Это работает, потому что «Нет» на встроенных функциях не является переключателем, оно не указано. / Oi- и все ваши проблемы исчезнут (это должно работать при оптимизации всей программы, но я не проверил это должным образом).

0 голосов
/ 30 августа 2010

Способ, которым "обычная" библиотека времени выполнения делает это, состоит в том, чтобы скомпилировать файл сборки с определением memset и связать его с библиотекой времени выполнения (Файл сборки можно найти в C: \ Program Files \ Microsoft Visual Studio или поблизости от него. 10,0 \ VC \ элт \ SRC \ Intel \ memset.asm). Такие вещи хорошо работают даже при оптимизации всей программы.

Также обратите внимание, что компилятор будет использовать встроенную функцию memset только в некоторых особых случаях (когда размер постоянен и мал?). Обычно он будет использовать предоставленную вами функцию memset, поэтому вам, вероятно, следует использовать оптимизированную функцию в memset.asm, если вы не собираетесь писать что-то столь же оптимизированное.

0 голосов
/ 31 мая 2010

Просто назовите функцию как-то немного по-другому.

...