0

我需要对 48 位变量进行一些操作,所以我有两个选择:

  1. 使用 48 位变量创建我自己的结构,或
  2. 使用unsigned 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会更好吗?
  • 有没有更好的方法来打包结构?

由于我现在正在展开循环,因此使用正确的数据结构可能会显着影响我的应用程序的性能。

谢谢你。

4

2 回答 2

1

尽管知道对 48 位值的操作不会溢出,但编译器无法知道这一点!此外,对于绝大多数编译器和平台,您的uint48结构实际上将实现为 64 位数据类型,只有低 48 位将被使用。

因此,在对数据进行任何算术(或其他)运算之后.v,需要清除(内部)64 位表示的“未使用”16 位,以确保将来对该数据的任何访问都会给出真实的,仅 48 位值。

因此,使用clang-clVisual 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编译器会生成非常相似的代码。

因此,您应该期望使用本机uint64_t变量将生成比您的“节省空间”结构更有效的代码。

于 2020-03-05T15:44:27.447 回答
0

你的测试程序是错误的。为什么?

  1. 打包 1 个成员结构实际上什么都不做。
  2. 您使用 -O0 执行它并且没有优化测试执行速度没有任何意义。如果您通过优化对其进行编译 - 您的代码将被清除:) https://godbolt.org/z/9ibP_8

当您将此代码排序为可优化时(因为您不使用该值,它们必须是全局的或至少是静态的并添加编译器内存屏障(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
于 2020-03-05T16:01:09.880 回答