Как прочитать 5 байтов в значимом uint64_t в C?

Мне нужно выделить массив uint64_t[1e9], чтобы что-то подсчитать, и я знаю, что элементы находятся между (0,2 ^ 39). Итак, я хочу calloc 5*1e9 байт для массива.

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

Должно быть 2 пути.

Один из них — сначала проверить порядок следования байтов, чтобы мы могли memcpy 5 байтов сделать первыми или последними из всех 8 байтов.

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

Я думаю, что первый должен быть быстрее.

Итак, в системе GCC, libc или GNU есть какой-либо заголовочный файл, указывающий, является ли текущая система Little Endian или Big Endian? Я знаю, что x86_64 — это Little Endian, но мне не нравится писать непереносимый код.

Конечно, любые другие идеи приветствуются.

Добавлять:

Мне нужно использовать массив для подсчета многих строк, используя хеширование D-left. Я планирую использовать 21 бит для ключа и 18 бит для подсчета.


person Galaxy    schedule 23.05.2011    source источник
comment
Скорее всего, ваша корневая проблема может быть решена таким образом, что вам не нужно заботиться о порядке байтов в вашем массиве. Например, опишите, как вы планируете использовать массив.   -  person Prof. Falken    schedule 23.05.2011
comment
Вы думаете, что memcpy должно быть быстрее, но вы не знаете. Есть потенциальные накладные расходы из-за вызова функции, а также цикл, который memcpy может выполнять внутри, по сравнению с развернутым циклом сдвигов и операций ИЛИ. Профиль сначала, чтобы получить реальные данные.   -  person jamesdlin    schedule 23.05.2011
comment
Вы не можете скопировать 5 байтов в первые или последние 5 байтов 8-байтового целого числа и ожидать, что это будет иметь дело с порядком байтов ваших данных. Вам придется поменять местами байты, чего memcpy не делает.   -  person nos    schedule 23.05.2011
comment
@nos: при использовании сдвигов и операций ИЛИ не требуется явная замена байтов.   -  person jamesdlin    schedule 23.05.2011
comment
@jamesdlin действительно, поэтому ОП должен делать именно это, а не пытаться использовать memcpy.   -  person nos    schedule 23.05.2011


Ответы (3)


Когда вы говорите «быстрее»… как часто выполняется этот код? 5 раз <<8 плюс |, вероятно, стоит менее 100 нс. Таким образом, если этот код выполняется 10 000 раз, это в сумме составляет 1 (одну) секунду.

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

Тем не менее, решение для определения endianess простое:

int a = 1;
char * ptr = (char*)&a;
bool littleEndian = *ptr == 1;

Теперь все, что вам нужно, это машина с обратным порядком байтов и пара тестов, чтобы убедиться, что ваше memcpy решение работает. Обратите внимание, что вам нужно вызвать memcpy пять раз в одном из двух случаев, чтобы изменить порядок байтов.

Или вы могли бы просто сдвинуть и или пять раз...

EDIT Думаю, я немного неправильно понял ваш вопрос. Вы говорите, что хотите использовать младшие 5 байтов (= 40 бит) uint64_t в качестве счетчика, да?

Таким образом, операция будет выполняться много-много раз. Опять же, memcpy совершенно бесполезен. Возьмем число 0x12345678 (32 бита). В памяти это выглядит так:

0x12 0x34 0x56 0x78    big endian
0x78 0x56 0x34 0x12    little endian

Как видите, байты поменялись местами. Таким образом, для преобразования между ними вы должны использовать битовый сдвиг или обмен байтами. memcpy не работает.

Но на самом деле это не имеет значения, поскольку ЦП будет выполнять декодирование за вас. Все, что вам нужно сделать, это переместить биты в нужном месте.

 key = item & 0x1FFFFF
 count = (item >>> 21)

читать и

 item = count << 21 | key

написать. Теперь вам просто нужно построить ключ из пяти байтов, и все готово:

 key = (((hash[0] << 8) | (hash[1]<<8)) | ....

ИЗМЕНИТЬ 2

Кажется, у вас есть массив 40-битных целых чисел, и вы хотите прочитать/записать этот массив.

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

Другое решение — использовать два массива:

int lower[];
unit8_t upper[]

то есть: Сохраните биты 33–40 в массиве second. Для чтения/записи значений необходим один Shift+or.

person Aaron Digulla    schedule 23.05.2011
comment
Если я использую memcpy, мне все равно, упорядочены ли 5 ​​байтов или наоборот. Поскольку единственная функция, которая делает их uint64_t, это memcpy. - person Galaxy; 23.05.2011
comment
Это неправильно. 1. вы также можете записывать байты прямо в память. 2. Чтобы исправить порядок байтов, нужно поменять местами байты. один вызов memcpy не может этого сделать, поэтому вам нужно вызвать его 5 раз. Поверьте мне. Я написал много кода с правильным порядком байтов. Все функции копирования памяти, которые предлагает stdlib, бесполезны. - person Aaron Digulla; 23.05.2011
comment
Я имею в виду, что мне не нужно держать 5 байтов в порядке, мне просто нужно фиксированное отображение для младших 5 байтов uint64_t. И я выбираю побитовый путь сейчас. - person Galaxy; 23.05.2011
comment
Я хочу спросить, как получить (uint64_t )item из uint40_t array[]. - person Galaxy; 23.05.2011
comment
Ах, это начинает иметь смысл. У вас есть массив байтов, и каждые пять байтов — это один элемент. Вопрос: каков самый быстрый способ прочитать 40-битный элемент int из массива в 64-битный int? - person Aaron Digulla; 23.05.2011

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

Сказав это, я действительно не понял, что вы пытаетесь сделать? Вам действительно нужен один миллиард записей, каждая из которых имеет длину пять байтов? Если данные, которые вы выбираете, разрежены, вам может сойти с рук выделение гораздо меньшего объема памяти.

person Lindydancer    schedule 23.05.2011

Ну, я просто обнаружил, что заголовки ядра идут с <asm/byteorder.h>.

встроенный memcpy в while(i<x+3){++*i=++*j} может быть еще медленнее, так как работа с кешем медленнее, чем с регистрами.

другой способ для memcpy:

union dat {
 uint64_t a;
 char b[8];
} d;
person Galaxy    schedule 23.05.2011