#c #optimization #clang #c11
#c #оптимизация #clang #c11
Вопрос:
Я тестирую различные варианты с clang 7/8 и c11.
Я реализовал некоторые функции сортировки и начал оптимизировать некоторые из них.
Функция сетевой сортировки — это уже очень простые части кода, но я обнаружил, что некоторое переписывание может обеспечить лучшую производительность на платформе x64 (не arm).
Если мы сравним обе сборки. Единственными заметными отличиями являются количество команд push и структура команд first mov :
network_sort_08_a:
push rbp
push r14
push rbx
mov esi, dword ptr [rdi]
mov eax, dword ptr [rdi 4]
mov edx, dword ptr [rdi 8]
mov r9d, dword ptr [rdi 12]
mov ebx, dword ptr [rdi 16]
cmp esi, eax
mov ecx, eax
cmovle ecx, esi
Вместо :
network_sort_8_b:
push rbp
push r15
push r14
push rbx
mov eax, dword ptr [rdi]
mov edx, dword ptr [rdi 4]
cmp eax, edx
mov ecx, edx
cmovle ecx, eax
cmovge edx, eax
mov eax, dword ptr [rdi 8]
Я хочу вывести первую версию без перезаписи функции.
Есть идеи, как этого добиться?
С наилучшими пожеланиями
__attribute__((always_inline))
inline int min(const int a, const int b)
{
return a < b ? a : b;
}
__attribute__((always_inline))
inline int max(const int a, const int b)
{
return a > b ? a : b;
}
__attribute__((always_inline))
inline void network_swap(int* keys, const int key_index0, const int key_index1)
{
const int key0 = keys[key_index0];
const int key1 = keys[key_index1];
const int min_key = min(key0, key1);
const int max_key = max(key0, key1);
keys[key_index0] = min_key;
keys[key_index1] = max_key;
}
void network_sort_08_a(int* keys)
{
// Resorted by dependencies
// Serialize first reads
const int key0 = keys[0];
const int key1 = keys[1];
const int key2 = keys[2];
const int key3 = keys[3];
const int key4 = keys[4];
const int key5 = keys[5];
const int key6 = keys[6];
const int key7 = keys[7];
keys[0] = min(key0, key1);
keys[1] = max(key0, key1);
keys[2] = min(key2, key3);
keys[3] = max(key2, key3);
keys[4] = min(key4, key5);
keys[5] = max(key4, key5);
keys[6] = min(key6, key7);
keys[7] = max(key6, key7);
network_swap(keys, 0, 2);
network_swap(keys, 1, 3);
network_swap(keys, 4, 6);
network_swap(keys, 5, 7);
network_swap(keys, 1, 2);
network_swap(keys, 5, 6);
network_swap(keys, 0, 4);
network_swap(keys, 3, 7);
network_swap(keys, 1, 5);
network_swap(keys, 1, 4);
network_swap(keys, 2, 6);
network_swap(keys, 3, 6);
network_swap(keys, 2, 4);
network_swap(keys, 3, 5);
network_swap(keys, 3, 4);
}
void network_sort_8_b(int* restrict keys)
{
network_swap(keys, 0, 1);
network_swap(keys, 2, 3);
network_swap(keys, 0, 2);
network_swap(keys, 1, 3);
network_swap(keys, 1, 2);
network_swap(keys, 4, 5);
network_swap(keys, 6, 7);
network_swap(keys, 4, 6);
network_swap(keys, 5, 7);
network_swap(keys, 5, 6);
network_swap(keys, 0, 4);
network_swap(keys, 1, 5);
network_swap(keys, 1, 4);
network_swap(keys, 2, 6);
network_swap(keys, 3, 7);
network_swap(keys, 3, 6);
network_swap(keys, 2, 4);
network_swap(keys, 3, 5);
network_swap(keys, 3, 4);
}
Комментарии:
1. Пожалуйста, укажите код, с которым вы пытаетесь работать, в вопросе. Ссылки на Godbolt могут со временем исчезнуть.
2. примечание: вы на самом деле профилировали и сравнивали, чтобы убедиться, что это медленно? В большинстве случаев компилятор выполняет довольно хорошую работу.
3. Я профилировал это на AMD Ryzen, и был фактический выигрыш (от 5 до 10%).
4. Если вы используете ubuntu или mingw, не стесняйтесь сравнивать github.com/meuns/sort-things ветви master и network_rewrite.