Не до конца понимаю пользовательскую функцию memcpy в C - PullRequest
0 голосов
/ 23 мая 2018

Итак, сегодня я просматривал исходный код движка Quake и наткнулся на некоторые написанные служебные функции.Одним из них был 'Q_memcpy':

void Q_memcpy (void *dest, void *src, int count)
{
    int             i;

    if (( ( (long)dest | (long)src | count) & 3) == 0 )
    {
        count>>=2;
        for (i=0 ; i<count ; i++)
            ((int *)dest)[i] = ((int *)src)[i];
    }
    else
        for (i=0 ; i<count ; i++)
            ((byte *)dest)[i] = ((byte *)src)[i];
}

Я понимаю всю предпосылку функции, но я не совсем понимаю причину побитового ИЛИ между адресом источника и назначения.Итак, сумма моих вопросов такова:

  • Почему «count» используется в одной и той же битовой арифметике?
  • Почему последние два бита результата проверяются, если они различаются?
  • Какую цель выполняет весь этот чек?

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

Ответы [ 3 ]

0 голосов
/ 23 мая 2018

Побитовое ORing и ANding с 3 должно проверять, делятся ли источник, назначение и count на 4. Если это так, операция может работать с 4-байтовыми словами, в то время как этот код принимает int как4 байта.В противном случае операция выполняется байтово.

0 голосов
/ 23 мая 2018

Он определяет, выровнены ли указатели источника и назначения int, и является ли count точным размером int байтов.

Если все эти три вещи верны, то все ls 2 бита будут 0 (при условии, что указатели и int равны 4 байта).Таким образом, алгоритм ИЛИ определяет три значения и выделяет биты ls 2.

В этом случае он копирует int на int.В противном случае он копирует char на char.

Если тест не пройден, более сложный алгоритм скопирует некоторые из начальных и конечных байтов char на char и промежуточные байты int наint.

0 голосов
/ 23 мая 2018

Сначала проверяется, делятся ли все 3 аргумента на 4. Если - и только если - все они равны, то выполняется копирование 4 байтов за раз.

Т.е. это не закодированобыло бы

if ((long) src % 4 == 0 && (long) dst % 4 == 0 && count % 4 == 0 )
{
    count = count / 4;
    for (i = 0; i < count; i++)
        ((int *)dest)[i] = ((int *)src)[i];
}

Я не уверен, тестировали ли они свой компилятор, и он генерировал плохой код даже для теста, и поэтому они решили написать его таким замысловатым способом.В любом случае x | y | z гарантирует, что в результате будет установлен бит n , если он установлен в любой из x, y или z.Поэтому, если (x | y | z) & 3 приводит к 0, ни в одном из чисел не было установлено ни одного из 2 младших битов, и поэтому оно делится на 4.


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

Следовательно, на последних компиляторах вы можете оптимизировать все вызовы Q_memcpy с помощьюпереключая их на memcpy.GCC может генерировать такие вещи, как 64-битные или SIMD-перемещения с memcpy в зависимости от размера области, которая будет скопирована.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...