самое быстрое сравнение массива u_int64_t [8] в C / C ++

0

Какой самый быстрый метод, чтобы сравнить два u_int64[8] в C/C++?

Массив 1 находится внутри элемента std::vector (~ 10k), массив 2 находится внутри динамически распределенной структуры. (is memcmp() здесь ложноположительно бесплатно?)

Моя (псевдо C) реализация:

typedef struct {            
    u_int64_t array[8];
}work_t;

/* alloc and fill array work_t* work = new (std::nothrow) work_t etc... */

for(u_int32_t i=0; i < some_std_vector.size(); i++) {       

                if((some_std_vector[i]->array[0] == work->array[0]) &&
                   (some_std_vector[i]->array[1] == work->array[1]) &&
                   (some_std_vector[i]->array[2] == work->array[2]) &&
                   (some_std_vector[i]->array[3] == work->array[3]) &&
                   (some_std_vector[i]->array[4] == work->array[4]) &&
                   (some_std_vector[i]->array[5] == work->array[5]) &&
                   (some_std_vector[i]->array[6] == work->array[6]) &&
                   (some_std_vector[i]->array[7] == work->array[7])) {
                    //...do some stuff...
                }
}

Целевой платформой является Linux x86_64 gcc 4.9.2, цикл находится внутри pthread, используется tcmalloc, а код скомпилирован с помощью -O2

  • 1
    Выше, вероятно, так же быстро или быстрее, чем использование memcmp, хотя многое зависит от конкретного компилятора и конструкции процессора.
  • 2
    memcmp() == 0 всегда в порядке, так как это необработанное сравнение памяти. Это не только хорошо, но и хороший совет для оптимизации компилятора (SSE, конвейерная обработка). Но действительно хороший компилятор будет интерпретировать ваш код так, как если бы он был memcmp() любом случае.
Теги:
arrays
stdvector
memcmp

4 ответа

1
Лучший ответ

Вот несколько советов по улучшению скорости.

Используйте локальные переменные, если это возможно

Вместо использования указателей, например, → operator, используйте локальные переменные или передайте переменные в качестве ссылок. Компилятор может генерировать дополнительный код для загрузки указателя в регистр, а затем разыгрывать регистр для получения значения.

Использование кэша данных процессора Большинство современных процессоров имеют кэш данных. Если вы можете загрузить несколько переменных с данными, а затем сравнить, вы можете вызвать кеш данных процессора.

Кроме того, создайте свои данные, чтобы эффективно входить в линию кэша данных. Это означает, что члены данных (включая массивы) должны быть рядом друг с другом или очень близко.

Блок Сравнить

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

Еще одно предложение - помочь компилятору загрузить значения в отдельные переменные, сравнивая значения:

for (/*...*/)
{
//...
    uint64_t a1 = some_std_vector[i]->array[0];
    uint64_t a2 = some_std_vector[i]->array[1];
    uint64_t a3 = some_std_vector[i]->array[2];
    uint64_t a4 = some_std_vector[i]->array[3];

    uint64_t b1 = work->array[0];
    uint64_t b2 = work->array[1];
    uint64_t b3 = work->array[2];
    uint64_t b4 = work->array[3];

    if ((a1 == b1) && (a2 == b2) && (a3 == b3) && (a4 == b4))
    {
       //...
    }
}

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

Обзор ассемблера и профиля

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

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

1

Я бы предположил, что единственным способом действительно ответить на этот вопрос было бы написать две процедуры: одну с помощью цикла, который вы предоставили, а другой - с помощью memcmp. Затем проанализируйте и посмотрите на сборку, чтобы увидеть, какая из них выглядит наиболее эффективной. (Вы также можете быть навязчивыми и использовать профилировщик.)

Можно также написать настраиваемую подпрограмму в сборке, чтобы сравнивать их напрямую (т.е. Пользовательскую версию memcmp, которая работает специально для сравнения именно того, что вы смотрите) и сравнить ее вместе с двумя другими.

В любом случае, я согласен с остальными, что все, вероятно, будет довольно близко (с современным компилятором); однако, если вы действительно хотели сохранить его, вам придется протестировать его с помощью профилировщика и/или иметь навыки, чтобы посмотреть созданную сборку и узнать, какой из них будет быстрее.

0

Я сделал несколько тестов и посмотрел на gcc memcmp, glibc memcmp и мой код выше. glibc-2.20 memcmp - это метод fastes, потому что он использует оптимизацию платформы (в моем случае).

gcc memcmp намного медленнее. (bug43052, скомпилировать с -fno-builtin-memcmp)

0

В зависимости от используемого вами устройства и используемого компилятора вы можете попробовать некоторые "конкретные" проблемы. Например, в некоторых компиляторах есть методы, которые позволяют выполнять большую нагрузку из памяти и, в результате, самые быстрые множественные сравнения. Также есть способы вручную развернуть цикл, чтобы они выполнялись быстрее. Но это зависит от компилятора. Вы всегда можете попробовать несколько способов и проверить код ассемблера, чтобы узнать, какой путь самый быстрый.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню