memcpy: GCC или оптимизация реализации? - PullRequest
0 голосов
/ 18 июня 2019

Относительно написания собственной функции memcpy для пользовательского загрузчика и ядра, я решил изучить различные аспекты написания хорошей и, возможно, быстрой реализации для копирования памяти на выровненных границах (например, прокрутка в режиме видео, где каждыйлиния на экране начинается с выровненной границы), но также для больших (> 1 МБ) и не выровненных структур.

Мой вопрос заключается в том, что компилятор, в моем случае GCC, поддерживает различные варианты оптимизации.параметры (либо путем включения отдельных параметров, либо с помощью O2, O3, ...), до какого уровня оптимизации мне нужно реализовать фактическую функцию memcpy для достижения наилучшего результата при копировании вместес флагами оптимизации GCC?

Моя текущая реализация выглядит следующим образом:

static void *memcpy_unaligned(void *dst, const void *src, size_t len)
{
    size_t i;
    unsigned char *d = (unsigned char *)dst;
    unsigned char *s = (unsigned char *)src;

    for (i = 0; i < len; i++)
        d[i] = s[i];

    return dst; 
}

static void *memcpy_aligned16(void *dst, const void *src, size_t len)
{
    size_t i;
    uint16_t *d = (uint16_t *)dst;
    uint16_t *s = (uint16_t *)src;

    for (i = 0; i < ((len) & (~1)); i += 2)
        d[i] = s[i];

    for ( ; i < len; i++)
        ((unsigned char *)d)[i] = ((unsigned char *)s)[i];

    return dst;
}

static void *memcpy_aligned32(void *dst, const void *src, size_t len)
{
    size_t i;
    uint32_t *d = (uint32_t *)dst;
    uint32_t *s = (uint32_t *)src;

    for (i = 0; i < ((len) & (~3)); i += 4)
        d[i] = s[i];

    for ( ; i < len; i++)
        ((unsigned char *)d)[i] = ((unsigned char *)s)[i];

    return dst;
}

static void *memcpy_aligned(void *dst, const void *src, size_t len)
{
    /* Are dst and src aligned on a 4-byte boundary? */
    if (ALIGNED(dst, src, 4))
        return memcpy_aligned32(dst, src, len);

    /* Are dst and src aligned on a 2-byte boundary? */
    if (ALIGNED(dst, src, 2))
        return memcpy_aligned16(dst, src, len);

    return memcpy_unaligned(dst, src, len);
}

void* memcpy(void *dst, const void *src, size_t len)
{
    return memcpy_aligned(dst, src, len);
}

Полезно ли также проверять, совпадают ли указатели dst и srcна нечетных границах только для первых или первых трех байтов, чтобы сначала сделать однобайтовую копию, а затем word и dword копирование?

1 Ответ

0 голосов
/ 18 июня 2019

Полезно ли проверять, выровнены ли указатели dst и src на нечетных границах только для первых или первых трех байтов, чтобы сначала сделать однобайтовую копию, а затем копировать слова и слова?

Профилирование для таких вопросов, но сначала получите правильную функцию.

В коде OP есть функциональные ошибки.

  • Оригинальный код OP тоже увеличивает индексfast
  • Примечание: (len) & (~ 3) могут некорректно маскировать редкие дополнения, отличные от 2

Используйте restrict, чтобы разрешить дополнительную оптимизацию.Примечание: memcpy() - это UB, когда буферы перекрываются.

static void *memcpy_aligned32(void * restrict dst, const void *restrict src, size_t len) {
  size_t i;
  // Casts not needed.  Do not cast away const-ness
  uint32_t *d = dst;
  const uint32_t *s = src;  

  size_t l4 = len/4;
  for (i = 0; i < l4; i++) {
    d[i] = s[i];
  }

  i *= 4;
  for ( ; i < len; i++) {
    ((unsigned char *)d)[i] = ((unsigned char *)s)[i]; // I'd use `uint8_t*` for symmetry
  }  

  return dst;
}

Возможно, применяются дополнительные проблемы с АА.

...