Самый быстрый способ получить нулевой символ в скопированной строке в C - PullRequest
4 голосов
/ 13 ноября 2010

Мне нужно получить указатель на завершающий нулевой символ строки.

В настоящее время я использую этот простой способ: MyString + strlen(MyString), что, вероятно, весьма неплохо вне контекста.

Однако мне не нравится это решение, так как я должен делать это после копирования строки:

char MyString[32];
char* EndOfString;
strcpy(MyString, "Foo");
EndOfString = MyString + strlen(MyString);

Итак, я дважды зацикливаюсь вокруг строки, первый раз в strcpy и второй раз в strlen.

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

size_t strcpylen(char *strDestination, const char *strSource)
{
    size_t len = 0;
    while( *strDestination++ = *strSource++ )
        len++;
    return len;
}

EndOfString = MyString + strcpylen(MyString, "Foobar");

Однако я боюсь, что моя реализация может быть медленнее, чем функция CRT, предоставляемая компилятором (которая может использовать некоторую оптимизацию сборки или другой прием вместо простого цикла char-by-char). Или, может быть, я не знаю какой-то стандартной встроенной функции, которая уже делает это?


Я провел несколько сравнений с бедными, повторив 0x1FFFFFFF три алгоритма (strcpy + strlen, мою версию strcpylen и версию user434507 ). Результат:

1) strcpy + strlen - победитель с 967 миллисекундами;

2) моя версия занимает гораздо больше времени: 57 секунд!

3) отредактированная версия занимает 53 секунды.

Таким образом, использование двух функций CRT вместо пользовательской «оптимизированной» версии в моей среде более чем в 50 раз быстрее!

Ответы [ 8 ]

5 голосов
/ 13 ноября 2010

Восторг Хакера имеет хороший раздел о поиске первого нулевого байта в строке C (см. Главу 6, раздел 1).Я нашел (частично) его в Google Книгах , и код, кажется, здесь .Я всегда возвращаюсь к этой книге.Надеюсь, это полезно.

5 голосов
/ 13 ноября 2010
size_t strcpylen(char *strDestination, const char *strSource)
{
    char* dest = strDestination;
    while( *dest++ = *strSource++ );
    return dest - strDestination;
}

Это почти то же самое, что делает CRT-версия strcpy, за исключением того, что CRT-версия также выполнит некоторую проверку, например. чтобы убедиться, что оба аргумента не равны нулю.

Редактировать: я смотрю на исходный код CRT для VC ++ 2005. pmg верен, проверки нет. Есть две версии strcpy. Один написан на ассемблере, другой на C. Вот версия C:

char * __cdecl strcpy(char * dst, const char * src)
{
        char * cp = dst;

        while( *cp++ = *src++ )
                ;               /* Copy src over dst */

        return( dst );
}
1 голос
/ 19 декабря 2010

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

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

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

Все зависит от стиля кода, приложения и места, в котором необходимо сохранять циклы.

Как видите, изображение немного сложнее, чем то, что было выставлено ранее.

1 голос
/ 13 ноября 2010

Вы можете попробовать это:

int len = strlen(new_str);
memcpy(MyString, new_str, len + 1);
EndOfString = MyString + len;

Это имеет смысл, только если new_str большой, потому что memcpy намного быстрее, чем стандартный while( *dest++ = *strSource++ ); подход, но требует дополнительных затрат на инициализацию.

1 голос
/ 13 ноября 2010

Используйте <a href="http://en.wikipedia.org/wiki/Strlcpy" rel="nofollow">strlcpy()</a>, который вернет длину скопированного файла (при условии, что ваш параметр размера достаточно большой).

0 голосов
/ 14 ноября 2010

Попробуйте memccpy() (или _memccpy() в VC 2005+). Я провел несколько тестов с strcpy + strlen и вашим пользовательским алгоритмом, и в моей среде он побил оба. Я не знаю, насколько хорошо он будет работать у вас, хотя, поскольку для меня ваш алгоритм работает намного быстрее, чем вы видели, и strcpy + strlen намного медленнее (14,4 с для первого против 7,3 с для второго, используя ваш номер итераций). Я набрал код ниже около 5 секунд.

int main(int argc, char *argv[])
{
    char test_string[] = "Foo";
    char new_string[64];
    char *null_character = NULL;
    int i;
    int iterations = 0x1FFFFFFF;

    for(i = 0; i < iterations; i++)
    {
        null_character = memccpy(new_string, test_string, 0, 64);
        --null_character;
    }

    return 0;
}
0 голосов
/ 13 ноября 2010

Я думаю, вы можете беспокоиться здесь излишне. Вполне вероятно, что любой возможный выигрыш, который вы можете получить здесь, будет более чем компенсирован лучшими улучшениями, которые вы можете сделать в другом месте. Я бы посоветовал не беспокоиться об этом, закончить код и посмотреть, неужели у вас такой недостаток циклов обработки, что польза от этой оптимизации перевешивает дополнительную работу и будущие усилия по обслуживанию, чтобы ускорить ее. 1003 *

Короче: не делай этого.

0 голосов
/ 13 ноября 2010
...