c# 联合与空指针性能[已关闭]

5f0d552i  于 2023-09-28  发布在  C#
关注(0)|答案(1)|浏览(218)

已关闭,此问题需要details or clarity。它目前不接受回答。
**想改善这个问题吗?**通过editing this post添加详细信息并澄清问题。

6小时前关闭
Improve this question
TL;DR在性能方面比空指针更好
当我在搜索联合与空指针性能时,我仔细研究了这个问题Union versus void pointer。许多人建议使用联合,但没有一个是因为性能。我的问题是,空指针是否比联合需要更多的时间,因为我们需要一次又一次地类型转换它。
我写了下面的代码来测试性能,发现union要好得多。

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

typedef struct union_test union_test;
typedef struct void_test void_test;
typedef void (*PrintUnionFunction)(union_test *);
typedef void (*PrintVoidFunction)(void_test *);

typedef enum ELEM_TYPE
{
    INT_TYPE,
    STRING_TYPE,
    FLOAT_TYPE
} ELEM_TYPE;

struct union_test
{
    ELEM_TYPE elemType;
    union {
        int **intElems;
        float **floatElems;
        char **stringElems;
    };
    size_t elemCount;
    PrintUnionFunction printFunction;
};

struct void_test
{
    ELEM_TYPE elemType;
    void **elems;
    size_t elemCount;
    PrintVoidFunction printFunction;
};

void printUnionInt(union_test *fpTest)
{
    for (size_t i = 0; i < fpTest->elemCount; i++)
    {
        int *temp = fpTest->intElems[i];
        (*temp)++;
        // printf("%d ", *fpTest->intElems[i]);
    }
}

void printVoidPointerInt(void_test *fpTest)
{
    for (size_t i = 0; i < fpTest->elemCount; i++)
    {
        int *temp = ((int *)fpTest->elems[i]);
        (*temp)++;
        // printf("%d ", *fpTest->intElems[i]);
    }
}

int main()
{
    clock_t start_time_union, end_time_union;
    clock_t start_time_void, end_time_void;
    size_t elemCount = 1024 * 1024 * 1024;

    union_test *fp = (union_test *)malloc(sizeof(union_test));
    fp->elemCount = elemCount;
    fp->elemType = INT_TYPE;
    fp->printFunction = printUnionInt;
    fp->intElems = (int **)malloc(sizeof(int *) * fp->elemCount);

    for (size_t i = 0; i < fp->elemCount; i++)
    {
        fp->intElems[i] = (int *)malloc(sizeof(int));
        memcpy(fp->intElems[i], &i, sizeof(int));
    }

    void_test *void_fp = (void_test *)malloc(sizeof(union_test));
    void_fp->elemCount = elemCount;
    void_fp->elemType = INT_TYPE;
    void_fp->printFunction = printVoidPointerInt;
    void_fp->elems = (void **)malloc(sizeof(void *) * void_fp->elemCount);

    for (size_t i = 0; i < void_fp->elemCount; i++)
    {
        void_fp->elems[i] = (int *)malloc(sizeof(int));
        memcpy(void_fp->elems[i], &i, sizeof(int));
    }

    start_time_union = clock();
    fp->printFunction(fp);
    end_time_union = clock();

    start_time_void = clock();
    void_fp->printFunction(void_fp);
    end_time_void = clock();

    printf("\n\nunion execution time: %f seconds\n", (double)(end_time_union - start_time_union) / CLOCKS_PER_SEC);
    printf("void pointer execution time: %f seconds\n", (double)(end_time_void - start_time_void) / CLOCKS_PER_SEC);

    return 0;
}

我得到了下面的结果。

union execution time: 8.237730 seconds
void pointer execution time: 8.647505 seconds

我在一些地方看到使用-O3将使编译器永远不会担心指针类型,并认为一切都只是内存字节,但我找不到任何改进的性能,即使使用-O3标志。
注意:我只关心性能,而不是可读性。我知道,当我们处理的类型数量有限时,使用联合是提高可读性的好方法。

1u4esq0p

1u4esq0p1#

您的代码在godbolt with gcc13.2 and -O3上生成以下程序集:

printUnionInt:
        mov     rdx, QWORD PTR [rdi+16]
        test    rdx, rdx
        je      .L1
        mov     rax, QWORD PTR [rdi+8]
        lea     rcx, [rax+rdx*8]
.L3:
        mov     rdx, QWORD PTR [rax]
        add     rax, 8
        add     DWORD PTR [rdx], 1
        cmp     rcx, rax
        jne     .L3
.L1:
        ret
printVoidPointerInt:
        mov     rdx, QWORD PTR [rdi+16]
        test    rdx, rdx
        je      .L9
        mov     rax, QWORD PTR [rdi+8]
        lea     rcx, [rax+rdx*8]
.L11:
        mov     rdx, QWORD PTR [rax]
        add     rax, 8
        add     DWORD PTR [rdx], 1
        cmp     rcx, rax
        jne     .L11
.L9:
        ret

我拿了一把尺子,测量了每个函数的长度,结果完全一样。没有什么区别。
您可能对https://quick-bench.com/感兴趣。
我的问题是,空指针是否比联合需要更多的时间,因为我们需要一次又一次地类型转换它。
在程序集中没有类型,寄存器就是寄存器。在C世界中,转换指针是一件事,在汇编中,这是一个无操作,什么也不会发生。

相关问题