自定义 malloc 实现上奇怪的操作系统相关行为

问题描述 投票:0回答:1

我正在学习一门课程,其中我们有一个malloc的自定义实现,取自这篇文章,malloc的实现可以在这里看到,此外,我们还有这个测试实现本身的程序(它只是进行了大量的内存分配)

#include <stdlib.h>

#define MAXITERS   100000
#define MAXSIZE    1024

int main(int argc, char* argv[]) {
  float pfree = atof(argv[1]);
  int niter = rand() % MAXITERS;
  for(int i = 0; i < niter; i++ ) {
     int size = rand() % MAXSIZE;
     void* p = malloc(size * sizeof(char));
     float toss = (float)random() / RAND_MAX;
     if ( toss > pfree )
        free(p);
  }
  return 0;
}

问题是,每当我运行它时,在 WSL2 上都会花费大约 30 秒。我朋友的电脑(配备 M3 Pro)大约需要 1 秒,我教授的 M2 Air 也花了大约 1 秒。自然,我认为这是 WSL 的问题,因此我快速启动到 Ubuntu 23,运行该程序,发现它与 WSl2 花费相同的时间。然后我决定在 Fedora 39 中运行它,并得到了相同的结果。

这是我们正在使用的编译命令,我认为这可能是某些事情变得奇怪的原因。

gcc -Wno-deprecated-declarations -o libmemalloc.so -fPIC -shared memalloc.c
export LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH
gcc testalloc.c -o testalloc -L. -lmemalloc
./testalloc 0.5

原始说明没有附带 LD_LIBRARY_PATH“export” 关键字,因为我的教授在 MacOS 上工作并且它似乎在那里工作,但是,在 Linux 上,没有它它们就无法工作。这可能是问题所在吗?导出功能与 MacOS 版本是否不同?

我会将包含我们正在运行的代码的 .zip 文件以及运行它的脚本上传到此存储库,这就是我用于测试的脚本。如果你们可以测试代码及其速度,并帮助我解决这个问题,我将不胜感激。

编辑 我们确认它确实是在 MacOS 上使用我们的实际实现,正如我们所补充的:

int x = x/0;
printf("%d", x);

对于代码,在 Linux 上它崩溃了,但奇怪的是,在 MacOS 上它只打印 0,我不知道这是否是正常行为,但无论哪种方式,它肯定使用我们的实现,因为它要么崩溃,要么打印 0 .

[我尝试过的]

尝试过其他计算机和操作系统,都一样(出于明显原因,Windows 除外)

尝试了不同的编译命令,包括原始文章中使用 LD_PRELOAD 的编译命令,但它们仍然非常慢,并且会破坏终端会话上的其他程序,而 LD_LIBRARY_PATH 似乎不会这样做。

添加破坏我们的 malloc 实现的代码(参见上面的编辑),运行我们的版本的 MacOS 大约需要 1~ 秒,但即使是 10 年前的笔记本电脑,在运行实际的 malloc() 时,这一事实进一步证明了这一点它在 0.0 秒内完成(使用 time 命令),因此 MacO 肯定正在运行我们版本的 malloc()。

c linux macos compilation malloc
1个回答
0
投票

程序的运行时间受到您获得的随机数的影响:

  1. niter
    的值(迭代次数!!!)
  2. 每个循环上
    size
    的值。
  3. toss
    的价值(无论您是否执行
    free
    )。

您正在混合

rand
random
调用 [为什么?]。

在某些系统上(例如

linux/glibc
),
rand
会调用
random
,但其他系统则不会。

此外,在

linux/glibc
上,
rand_r
是一个简单的线性同余生成器,而
rand
不是
rand_r(&seed)

参见:线性同余发生器

因此,为了获得一致的结果,您将受到给定系统实现

rand
random

的影响

尝试将所有

rand*
调用替换为:(例如)
xrand
。这是基于“标准”数字的 LCG:

static unsigned int xrand_seed;

int
xrand_r(unsigned int *seed)
{
    int val = *seed;
    val = (val * 1103515245) + 12345;
    *seed = val;
    return val & 0x7FFFFFFF;
}

int
xrand(void)
{
    return xrand_r(&xrand_seed);
}

这是

testalloc.c
的重构版本:

  1. 它使用上面的
    xrand
    实现
  2. 它具有调试功能
    printf
    和时间戳,以便您可以看到性能如何受到影响。
  3. 它允许使用
    -R<seed>
    参数来查看随机种子如何影响性能。
  4. 在没有参数的情况下调用程序导致的段错误已修复。
/* * a simple application to exercise malloc() and free() calls */ #include <stdlib.h> #include <stdio.h> #include <time.h> #define MAXITERS 100000 #define MAXSIZE 1024 #if DEBUG #define dbgprt(_fmt...) \ do { \ dbgstamp(); \ printf(_fmt); \ } while (0) #else #define dbgprt(_fmt...) \ do { } while (0) #endif double tsczero; double tscgetf(void) { struct timespec ts; double sec; clock_gettime(CLOCK_MONOTONIC,&ts); sec = ts.tv_nsec; sec /= 1e9; sec += ts.tv_sec; sec -= tsczero; return sec; } void dbgstamp(void) { double tsc = tscgetf(); static unsigned long long seqno; printf("[%llu/%.9f] ",seqno++,tsc); } static unsigned int xrand_seed; int xrand_r(unsigned int *seed) { int val = *seed; val = (val * 1103515245) + 12345; *seed = val; return val & 0x7FFFFFFF; } int xrand(void) { return xrand_r(&xrand_seed); } int main(int argc, char *argv[]) { --argc; ++argv; tsczero = tscgetf(); for (; argc > 0; --argc, ++argv) { char *cp = *argv; if (*cp != '-') break; cp += 2; switch (cp[-1]) { case 'R': xrand_seed = atoll(cp); break; } } dbgprt("main: R=%u\n",xrand_seed); double pfree; if (argc > 1) pfree = atof(argv[1]); else pfree = 0.5; dbgprt("main: pfree=%g\n",pfree); int niter = xrand() % MAXITERS; dbgprt("main: niter=%d\n",niter); for (int i = 0; i < niter; i++) { dbgprt("main: ENTER i=%d/%d\n",i,niter); int size = xrand() % MAXSIZE; dbgprt("main: RAND size=%d\n",size); void *p = malloc(size * sizeof(char)); dbgprt("main: RET p=%p\n",p); double toss = (double) xrand() / RAND_MAX; if (toss > pfree) { dbgprt("main: FREE\n"); free(p); } dbgprt("main: EXIT\n"); } printf("Successful\n"); return 0; }
    
© www.soinside.com 2019 - 2024. All rights reserved.