почему производительность strcpy в glibc хуже? - PullRequest
3 голосов
/ 08 сентября 2011

Я читаю исходный код для glibc2.9. Считая исходный код функции strcpy, производительность не так хороша, как я ожидаю.

Ниже приведен исходный код strcpy в glibc2.9:

   char * strcpy (char *dest, const char* src)
    {
        reg_char c;
        char *__unbounded s = (char *__unbounded) CHECK_BOUNDS_LOW (src);
        const ptrdiff_t off = CHECK_BOUNDS_LOW (dest) - s - 1;
        size_t n;

        do {
            c = *s++;
            s[off] = c;
        }
        while (c != '\0');

        n = s - src;
        (void) CHECK_BOUNDS_HIGH (src + n);
        (void) CHECK_BOUNDS_HIGH (dest + n);

        return dest;
    }

Поскольку я не знаю причину использования смещения, я провел несколько тестов производительности, сравнив приведенный выше код со следующим кодом:

char* my_strcpy(char *dest, const char *src)
{
    char *d = dest;
    register char c;

    do {
        c = *src++;
        *d++ = c;
    } while ('\0' != c);

    return dest;
}

В результате производительность strcpy хуже во время моих тестов. Я удалил коды привязанного указателя.

Почему версия glibc использует смещения ??

Ниже приводится введение о тестах.

  1. платформа: x86 (Intel (R) Pentium (R) 4), версия gcc 4.4.2
  2. флаг компиляции: нет флагов, потому что я не хочу никакой оптимизации; Команда: gcc test.c.

Код теста, который я использовал, следующий:

#include <stdio.h>
#include <stdlib.h>

char* my_strcpy1(char *dest, const char *src)
{
    char *d = dest;
    register char c;

    do {
        c = *src++;
        *d++ = c;
    } while ('\0' != c);

    return dest;
}

/* Copy SRC to DEST. */
char *
my_strcpy2 (dest, src)
     char *dest;
     const char *src;
{
  register char c;
  char * s = (char *)src;
  const int off = dest - s - 1;

  do
    {
      c = *s++;
      s[off] = c;
    }
  while (c != '\0');

  return dest;
}

int main()
{
    const char str1[] = "test1";
    const char str2[] = "test2";
    char buf[100];

    int i;
    for (i = 0; i < 10000000; ++i) {
        my_strcpy1(buf, str1);
        my_strcpy1(buf, str2);
    }

    return 0;
}

При использовании функции my_strcpy1 выходы:

[root@Lnx99 test]#time ./a.out

real    0m0.519s
user    0m0.517s
sys     0m0.001s
[root@Lnx99 test]#time ./a.out

real    0m0.520s
user    0m0.520s
sys     0m0.001s
[root@Lnx99 test]#time ./a.out

real    0m0.519s
user    0m0.516s
sys     0m0.002s

При использовании my_strcpy2 вывод:

[root@Lnx99 test]#time ./a.out

real    0m0.647s
user    0m0.647s
sys     0m0.000s
[root@Lnx99 test]#time ./a.out

real    0m0.642s
user    0m0.638s
sys     0m0.001s
[root@Lnx99 test]#time ./a.out

real    0m0.639s
user    0m0.638s
sys     0m0.002s

Я знаю, что это не очень точно с командой time. Но я мог бы получить ответ от пользователя времени.

Обновление:

To remove the cost used to calculate the offset, I removed some code and added a global variable.

#include <stdio.h>
#include <stdlib.h>

char* my_strcpy1(char *dest, const char *src)
{
    char *d = dest;
    register char c;

    do {
        c = *src++;
        *d++ = c;
    } while ('\0' != c);

    return dest;
}


int off;

/* Copy SRC to DEST. */
char *
my_strcpy2 (dest, src)
     char *dest;
     const char *src;
{
  register char c;
  char * s = (char *)src;

  do
    {
      c = *s++;
      s[off] = c;
    }
  while (c != '\0');

  return dest;
}

int main()
{
    const char str1[] = "test1test1test1test1test1test1test1test1";
    char buf[100];

    off = buf-str1-1;

    int i;
    for (i = 0; i < 10000000; ++i) {
        my_strcpy2(buf, str1);
    }

    return 0;
}

Но производительность my_strcpy2 все еще хуже, чем my_strcpy1. Затем я проверил собранный код, но тоже не смог получить ответ.

Я также увеличил размер строки, и производительность my_strcpy1 все еще лучше, чем my_strcpy2

Ответы [ 3 ]

8 голосов
/ 08 сентября 2011

Используется метод смещения, потому что это исключает один шаг из цикла - код glibc должен увеличивать только s, тогда как ваш код должен увеличивать как s, так и d.

Примечание.что код, который вы просматриваете, является независимой от архитектуры реализацией резервной реализации - glibc имеет переопределенные реализации сборок для многих архитектур (например, x86-64 strcpy()).

1 голос
/ 15 февраля 2014

Вот моя собственная оптимизация strcpy.Я думаю, что он имел 2x-3x ускорение по сравнению с наивной реализацией, но его нужно сравнить.

https://codereview.stackexchange.com/questions/30337/x86-strcpy-can-this-be-shortened/30348#30348

1 голос
/ 08 сентября 2011

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

Посмотрите на цикл, и ваш цикл, и цикл glibc практически идентичны.Но у glibc есть дополнительный код до и после ...

Как правило, простые смещения не снижают производительность, поскольку x86 допускает довольно сложную схему косвенной адресации.Таким образом, оба цикла здесь, вероятно, будут работать с одинаковой скоростью.

РЕДАКТИРОВАТЬ: Вот мое обновление с добавленной вами информацией.

Ваш размер строки составляет всего 5 символов.Несмотря на то, что метод смещения «может» быть немного быстрее в долгосрочной перспективе, тот факт, что ему требуется несколько операций для вычисления смещения до запуска цикла, замедляет его для коротких строк.Возможно, если вы попробуете более крупные струны, разрыв уменьшится и, возможно, исчезнет совсем.

...