Загрузка ЦП C Упакованная структура против беззнаковых длинных длинных операций - PullRequest
0 голосов
/ 05 марта 2020

Мне нужно выполнить некоторые операции с 48-битными переменными, поэтому у меня было два варианта:

  1. Создать собственную структуру с 48-битными переменными или
  2. Использовать без знака long long (64 бита).

Поскольку операции не будут переполнены 48 битами, я посчитал, что использование 64-битных переменных было излишним, поэтому я создал базовую структуру

#ifdef __GNUC__
#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
#endif

#ifdef _MSC_VER
#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop))
#endif

PACK(struct uint48 {
    unsigned long long v : 48;
});

и создал некоторый код для проверки для скорости в операциях

#include <stdio.h>
#include <time.h>

#ifdef __GNUC__
#define PACK( __Declaration__ ) __Declaration__ __attribute__((__packed__))
#endif

#ifdef _MSC_VER
#define PACK( __Declaration__ ) __pragma( pack(push, 1) ) __Declaration__ __pragma( pack(pop))
#endif

PACK(struct uint48 {
    unsigned long long v : 48;
});


void TestProductLong();
void TestProductLong02();

void TestProductPackedStruct();
void TestProductPackedStruct02();

clock_t start, end;
double cpu_time_used;
int cycleNumber = 100000;

int main(void)
{
    TestProductLong();
    TestProductLong02();

    TestProductPackedStruct();
    TestProductPackedStruct02();

    return 0;
}


void TestProductLong() {

    start = clock();

    for (int i = 0; i < cycleNumber;i++) {
        unsigned long long varlong01 = 155782;
        unsigned long long varlong02 = 15519994;
        unsigned long long product01 = varlong01 * varlong02;

        unsigned long long varlong03 = 155782;
        unsigned long long varlong04 = 15519994;
        unsigned long long product02 = varlong03 * varlong04;

        unsigned long long addition = product01 + product02;
    }

    end = clock();
    cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;

    printf("TestProductLong() took %f seconds to execute \n", cpu_time_used);
}


void TestProductLong02() {

    start = clock();

    unsigned long long varlong01;
    unsigned long long varlong02;
    unsigned long long product01;

    unsigned long long varlong03;
    unsigned long long varlong04;
    unsigned long long product02;

    unsigned long long addition;

    for (int i = 0; i < cycleNumber;i++) {
        varlong01 = 155782;
        varlong02 = 15519994;
        product01 = varlong01 * varlong02;

        varlong03 = 155782;
        varlong04 = 15519994;
        product02 = varlong03 * varlong04;

        addition = product01 + product02;
    }

    end = clock();
    cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;

    printf("TestProductLong02() took %f seconds to execute \n", cpu_time_used);
}


void TestProductPackedStruct() {

    start = clock();

    for (int i = 0; i < cycleNumber; i++) {
        struct uint48 x01;
        struct uint48 x02;
        struct uint48 x03;

        x01.v = 155782;
        x02.v = 15519994;
        x03.v = x01.v * x02.v;

        struct uint48 x04;
        struct uint48 x05;
        struct uint48 x06;

        x04.v = 155782;
        x05.v = 15519994;
        x06.v = x04.v * x05.v;

        struct uint48 x07;

        x07.v = x03.v + x06.v;
    }

    end = clock();
    cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;

    printf("TestProductPackedStruct() took %f seconds to execute \n", cpu_time_used);
}


void TestProductPackedStruct02() {

    start = clock();

    struct uint48 x01;
    struct uint48 x02;
    struct uint48 x03;
    struct uint48 x04;
    struct uint48 x05;
    struct uint48 x06;
    struct uint48 x07;

    for (int i = 0; i < cycleNumber; i++) {

        x01.v = 155782;
        x02.v = 15519994;
        x03.v = x01.v * x02.v;

        x04.v = 155782;
        x05.v = 15519994;
        x06.v = x04.v * x05.v;

        x07.v = x03.v + x06.v;
    }

    end = clock();
    cpu_time_used = ((double)(end - start)) / CLOCKS_PER_SEC;

    printf("TestProductPackedStruct02() took %f seconds to execute \n", cpu_time_used);
}

Но я получил следующие результаты

TestProductLong() took 0.000188 seconds to execute 
TestProductLong02() took 0.000198 seconds to execute 
TestProductPackedStruct() took 0.001231 seconds to execute 
TestProductPackedStruct02() took 0.001231 seconds to execute

Поэтому операции, использующие unsigned long long , занимали меньше времени, чем операции, использующие упакованная структура.

  • Почему это?
  • Лучше бы вместо этого использовать unsigned long long ?
  • Есть ли лучший способ упаковать структуры?

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

Спасибо.

Ответы [ 2 ]

1 голос
/ 05 марта 2020

Хотя вы знаете, что операции над 48-битными значениями не будут переполнены, компилятор не может знать это! Кроме того, в подавляющем большинстве компиляторов и платформ ваша структура uint48 будет фактически реализована как 64-битный тип данных, для которого когда-либо будут использоваться только младшие 48 бит.

Итак, после При любых арифметических c (или других) операциях с данными .v необходимо будет очистить «неиспользуемые» 16-битные (внутреннего) 64-битного представления, чтобы обеспечить доступ к этим данным в будущем. истинное 48-разрядное значение.

Таким образом, используя компилятор clang-cl в Visual Studio 2019, следующую (довольно тривиальную) функцию с использованием собственного типа uint64_t:

extern uint64_t add64(uint64_t a, uint64_t b) {
    return a + b;
}

генерирует ожидаемый высокоэффективный код сборки:

    lea rax, [rcx + rdx]
    ret

Однако, для использования (эквивалента) вашей 48-битной упакованной структуры:

#pragma pack(push, 1)
typedef struct uint48 {
    unsigned long long v : 48;
} uint48_t;
#pragma pack(pop)

extern uint48_t add48(uint48_t a, uint48_t b) {
    uint48_t c;
    c.v = a.v + b.v;
    return c;
}

требуется дополнительный код сборки для убедитесь, что любое переполнение в «неиспользуемые» биты отбрасывается:

    add rcx, rdx
    movabs  rax, 281474976710655  # This is 0x0000FFFFFFFFFFFF - clearing top 16 bits!
    and rax, rcx
    ret

Обратите внимание, что компилятор MSVC генерирует очень похожий код.

Таким образом, вы должны ожидать, что при использовании native, uint64_t переменная Файлы будут генерировать более эффективный код, чем ваша «экономящая место» структура.

0 голосов
/ 05 марта 2020

Ваша тестовая процедура неверна. Почему?

  1. Структура элемента упаковки 1 фактически ничего не делает.
  2. Вы выполняете его, используя -O0, и без оптимизации, тестирование скорости выполнения не имеет никакого смысла. Если вы скомпилируете его с оптимизацией - ваш код будет уничтожен :) https://godbolt.org/z/9ibP_8

Когда вы сортируете этот код для оптимизации (поскольку вы не используете значение, которое они должен быть глобальным или, по крайней мере, stati c и с добавлением барьера памяти компилятора (clobber).

https://godbolt.org/z/BL9uJE

Разница заключается в сокращении результатов до 48 бит.

Если вы упаковываете структуру (которая здесь не обязательна), вы заставляете компилятор обращаться к переменным байтами - потому что всегда выровнены только байты: https://godbolt.org/z/2iV7vq

Вы также можете использовать смешанный подход - не переносимый, так как он основан на реализации порядка байтов и байтов https://godbolt.org/z/J3-it_

, поэтому код будет компилироваться в: unsigned long long:

        mov     QWORD PTR varlong01[rip], 155782
        mov     QWORD PTR varlong02[rip], 15519994
        mov     QWORD PTR product01[rip], rdx
        mov     QWORD PTR varlong03[rip], 155782
        mov     QWORD PTR varlong04[rip], 15519994
        mov     QWORD PTR product02[rip], rdx
        mov     QWORD PTR addition[rip], rcx

не упакованная структура

        mov     rdx, QWORD PTR x01[rip]
        and     rdx, rax
        or      rdx, 155782
        mov     QWORD PTR x01[rip], rdx
        mov     rdx, QWORD PTR x02[rip]
        and     rdx, rax
        or      rdx, 15519994
        mov     QWORD PTR x02[rip], rdx
        mov     rdx, QWORD PTR x03[rip]
        and     rdx, rax
        or      rdx, rsi
        mov     QWORD PTR x03[rip], rdx
        mov     rdx, QWORD PTR x04[rip]
        and     rdx, rax
        or      rdx, 155782
        mov     QWORD PTR x04[rip], rdx
        mov     rdx, QWORD PTR x05[rip]
        and     rdx, rax
        or      rdx, 15519994
        mov     QWORD PTR x05[rip], rdx
        mov     rdx, QWORD PTR x06[rip]
        and     rdx, rax
        or      rdx, rsi
        mov     QWORD PTR x06[rip], rdx
        mov     rdx, QWORD PTR x07[rip]
        and     rdx, rax
        or      rdx, rdi
        mov     QWORD PTR x07[rip], rdx

упакованная структура

        mov     BYTE PTR x01[rip], -122
        mov     BYTE PTR x01[rip+1], 96
        mov     BYTE PTR x01[rip+2], 2
        mov     BYTE PTR x01[rip+3], 0
        mov     BYTE PTR x01[rip+4], 0
        mov     BYTE PTR x01[rip+5], 0
        mov     BYTE PTR x02[rip], -6
        mov     BYTE PTR x02[rip+1], -48
        mov     BYTE PTR x02[rip+2], -20
        mov     BYTE PTR x02[rip+3], 0
        mov     BYTE PTR x02[rip+4], 0
        mov     BYTE PTR x02[rip+5], 0
        mov     BYTE PTR x03[rip], -36
        mov     BYTE PTR x03[rip+1], 34
        mov     BYTE PTR x03[rip+2], 71
        mov     BYTE PTR x03[rip+3], -20
        mov     BYTE PTR x03[rip+4], 50
        mov     BYTE PTR x03[rip+5], 2
        mov     BYTE PTR x04[rip], -122
        mov     BYTE PTR x04[rip+1], 96
        mov     BYTE PTR x04[rip+2], 2
        mov     BYTE PTR x04[rip+3], 0
        mov     BYTE PTR x04[rip+4], 0
        mov     BYTE PTR x04[rip+5], 0
        mov     BYTE PTR x05[rip], -6
        mov     BYTE PTR x05[rip+1], -48
        mov     BYTE PTR x05[rip+2], -20
        mov     BYTE PTR x05[rip+3], 0
        mov     BYTE PTR x05[rip+4], 0
        mov     BYTE PTR x05[rip+5], 0
        mov     BYTE PTR x06[rip], -36
        mov     BYTE PTR x06[rip+1], 34
        mov     BYTE PTR x06[rip+2], 71
        mov     BYTE PTR x06[rip+3], -20
        mov     BYTE PTR x06[rip+4], 50
        mov     BYTE PTR x06[rip+5], 2
        mov     BYTE PTR x07[rip], -72
        mov     BYTE PTR x07[rip+1], 69
        mov     BYTE PTR x07[rip+2], -114
        mov     BYTE PTR x07[rip+3], -40
        mov     BYTE PTR x07[rip+4], 101
        mov     BYTE PTR x07[rip+5], 4
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...