gpt4 book ai didi

C#调用native代码比native调用native快

转载 作者:太空狗 更新时间:2023-10-29 16:56:38 26 4
gpt4 key购买 nike

在进行一些性能测试时,我遇到了一种我似乎无法解释的情况。

我编写了以下 C 代码:

void multi_arr(int32_t *x, int32_t *y, int32_t *res, int32_t len)
{
for (int32_t i = 0; i < len; ++i)
{
res[i] = x[i] * y[i];
}
}

我使用 gcc 将其与测试驱动程序一起编译为单个二进制文件。我还使用 gcc 将它自己编译成一个共享对象,我通过 p/invoke 从 C# 调用它。目的是测量从 C# 调用 native 代码的性能开销。

在 C 和 C# 中,我创建等长的随机值输入数组,然后测量运行 multi_arr 需要多长时间。在 C# 和 C 中,我都使用 POSIX clock_gettime() 调用进行计时。我将计时调用定位在对 multi_arr 的调用之前和之后,因此输入准备时间等不会影响结果。我运行 100 次迭代并报告平均时间和最小时间。

尽管 C 和 C# 执行完全相同的功能,但 C# 大约有 50% 的时间领先,通常是相当可观的。例如,对于 1,048,576 的 len,C# 的最小值为 768,400 ns,而 C 的最小值为 1,344,105。 C# 的平均值是 1,018,865,而 C 的平均值是 1,852,880。我在这张图中放入了一些不同的数字(注意对数刻度):

enter image description here

这些结果对我来说似乎非常错误,但工件在多个测试中是一致的。我已经检查了 asm 和 IL 以验证正确性。位数是一样的。我不知道什么会影响性能到这种程度。我放了一个最小的复制示例 here .

这些测试都在 Linux(KDE neon,基于 Ubuntu Xenial)上运行,dotnet-core 2.0.0 和 gcc 5.0.4。

有人见过这个吗?

最佳答案

正如您已经怀疑的那样,它取决于对齐方式。返回内存,以便编译器可以将其用于在存储或检索数据类型(例如 double 或整数)时不会导致不必要的错误的结构,但它不保证内存块如何适合缓存。

这会如何变化取决于您测试的硬件。假设您在这里谈论的是 x86_64,这意味着 Intel 或 AMD 处理器及其缓存相对于主内存访问的相对速度。

您可以通过使用各种对齐方式进行测试来模拟这一点。

这是我拼凑的示例程序。在我的 i7 上,我看到了很大的变化,但第一个最不对齐的访问确实比更对齐的版本慢。

#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

void multi_arr(int32_t *x, int32_t *y, int32_t *res, int32_t len)
{
for (int32_t i = 0; i < len; ++i)
{
res[i] = x[i] * y[i];
}
}

uint64_t getnsec()
{
struct timespec n;

clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &n);
return (uint64_t) n.tv_sec * 1000000000 + n.tv_nsec;
}

#define CACHE_SIZE (16 * 1024 * 1024 / sizeof(int32_t))
int main()
{
int32_t *memory;
int32_t *unaligned;
int32_t *x;
int32_t *y;
int count;
uint64_t start, elapsed;
int32_t len = 1024 * 16;
int64_t aligned = 1;

memory = calloc(sizeof(int32_t), 4 * CACHE_SIZE);

// make unaligned as unaligned as possible, e.g. to 0b11111111111111100

unaligned = (int32_t *) (((intptr_t) memory + CACHE_SIZE) & ~(CACHE_SIZE - 1));
printf("memory starts at %p, aligned %p\n", memory, unaligned);
unaligned = (int32_t *) ((intptr_t) unaligned | (CACHE_SIZE - 1));
printf("memory starts at %p, unaligned %p\n", memory, unaligned);

for (aligned = 1; aligned < CACHE_SIZE; aligned <<= 1)
{
x = (int32_t *) (((intptr_t) unaligned + CACHE_SIZE) & ~(aligned - 1));

start = getnsec();
for (count = 1; count < 1000; count++)
{
multi_arr(x, x + len, x + len + len, len);
}
elapsed = getnsec() - start;
printf("memory starts at %p, aligned %08"PRIx64" to > cache = %p elapsed=%"PRIu64"\n", unaligned, aligned - 1, x, elapsed);
}

exit(0);
}

关于C#调用native代码比native调用native快,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46394497/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com