Хотите использовать ASM для быстрой 8-байтовой выровненной копии массива вместо memmove - PullRequest
1 голос
/ 22 октября 2011

У меня есть массив структур, размер которых соответствует 8-байтовой границе.Мне нужно перемещать данные большими порциями внутри самого массива, поэтому я использую memmove ().Это работает, но это очень медленно.Я думаю, что компилятор не оптимизирует функцию для копирования 4 или 8 байтов за раз, отсюда и задержка.

Я бы предпочел сделать принудительное копирование с помощью int32_t или int64_t vars.Таким образом, я могу получить копию memcpy 4 или 8 байт за раз, что ускоряет процесс.Это будет работать нормально, так как мои структуры всегда имеют размер до 8 байтов.

Я не могу найти способ форсировать это в C. Я пытался сделать это с помощью встроенной сборки, но я не знаюкак указать операнды на конкретные элементы массива.Например, если мой оператор ASM копирует 4 байта за раз, мне нужно увеличить массив на 4 байта.Я не знаю, как это сделать.Вот что я думаю:

//here's our 2048 byte struct
typedef struct {
    filename[1024];
    description[1024];
} RECORD;

//total number of rows, or elements
int row_count = 0;

//create initial record
RECORD *record = (RECORD*)malloc(sizeof(RECORD));

//insert some stuff
strcpy(record->filename,"filename.txt");
strcpy(record->description,"Description of file");

//increment our row count
row_count++;

//now let's add a row
record = (RECORD*)realloc(record,sizeof(RECORD)*(row_count+1));

//duplicate first record
//copy first 4 bytes from "record" to the newly appended row
//obviously this would be a loop copying 4 bytes at a time
//up to the the size of the row, which is 2048 bytes.
__asm__("movl (%1), %%eax; \n\t"
    "movl %%eax, (%0); \n\t"
    : "=r"(record+row_count)    //output
    :  "r"(record+0)            //input
    : "%eax" );                 //list of registers used

//Don't work. :-(

1 Ответ

4 голосов
/ 22 октября 2011

Как отметил @Vlad, memmove & memcpy в целом высоко оптимизированы, в наши дни они обычно реализуются с SIMD для больших блоков, это означает, что вы должны действительно профилировать свой код, прежде чем тратить время на оптимизацию того, что вы думаю, что является узким местом.

На ваш актуальный вопрос: у вас нет зацикливания в вашей копии, однако лучше использовать что-то вроде REP MOVSD для 4-байтовых за раз или REP MOVSQ на x64 для 8-байтовых за раз. однако, увидев, что ваши данные выровнены на 8 байт, вы даже можете использовать MMX для копирования через MOVQ , что будет делать 64 бита за раз.

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

void MyMemCopy(void* pSrc, void* pDst, int nElements)
{
    int64_t* s = (int64_t*)pSrc;
    int64_t* d = (int64_t*)pDst;
    while(nElements--)
        *d++ = *s++;
}

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

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