【问题标题】:Can some explain the performance behavior of the following memory allocating C program?有人可以解释以下内存分配 C 程序的性能行为吗?
【发布时间】:2012-04-10 20:52:29
【问题描述】:

在我的机器上,时间 A 和时间 B 交换取决于 A 是否为 定义与否(这会改变调用两个callocs 的顺序)。

我最初将此归因于分页系统。奇怪的是,当 使用mmap 而不是calloc,情况更加奇怪——正如预期的那样,两个循环花费的时间相同。作为 可以看到stracecallocs 最终导致两个 mmaps,所以没有返回已分配内存的魔法。

我在 Intel i7 上运行 Debian 测试。

#include <stdlib.h>
#include <stdio.h>
#include <sys/mman.h>

#include <time.h>

#define SIZE 500002816

#ifndef USE_MMAP
#define ALLOC calloc
#else
#define ALLOC(a, b) (mmap(NULL, a * b, PROT_READ | PROT_WRITE,  \
                          MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))
#endif

int main() {
  clock_t start, finish;
#ifdef A
  int *arr1 = ALLOC(sizeof(int), SIZE);
  int *arr2 = ALLOC(sizeof(int), SIZE);
#else
  int *arr2 = ALLOC(sizeof(int), SIZE);
  int *arr1 = ALLOC(sizeof(int), SIZE);
#endif
  int i;

  start = clock();
  {
    for (i = 0; i < SIZE; i++)
      arr1[i] = (i + 13) * 5;
  }
  finish = clock();

  printf("Time A: %.2f\n", ((double)(finish - start))/CLOCKS_PER_SEC);

  start = clock();
  {
    for (i = 0; i < SIZE; i++)
      arr2[i] = (i + 13) * 5;
  }
  finish = clock();

  printf("Time B: %.2f\n", ((double)(finish - start))/CLOCKS_PER_SEC);

  return 0;
}

我得到的输出:

 ~/directory $ cc -Wall -O3 bench-loop.c -o bench-loop
 ~/directory $ ./bench-loop 
Time A: 0.94
Time B: 0.34
 ~/directory $ cc -DA -Wall -O3 bench-loop.c -o bench-loop
 ~/directory $ ./bench-loop                               
Time A: 0.34
Time B: 0.90
 ~/directory $ cc -DUSE_MMAP -DA -Wall -O3 bench-loop.c -o bench-loop
 ~/directory $ ./bench-loop                                          
Time A: 0.89
Time B: 0.90
 ~/directory $ cc -DUSE_MMAP -Wall -O3 bench-loop.c -o bench-loop 
 ~/directory $ ./bench-loop                                      
Time A: 0.91
Time B: 0.92

【问题讨论】:

  • 您应该(清楚地)在代码块之外提出您的问题。将其隐藏在代码 cmets 中没有帮助。
  • 可能想要删除 C++ 标记以支持 *nix。您能否更具体地了解您要查找的内容?基本上它要么使用内存映射文件,要么使用常规分配...
  • +1:我之前在进行性能测量时遇到过完全相同的现象。我一直没有弄清楚发生了什么,所以我真的希望你能得到一个好的答案。
  • 不考虑calloc 所花费的时间是不公平的。由于您正在跟踪您知道与内存管理、分页、页面缓存等相关的问题,并通过挂钟时间对其进行计时,因此您不能排除内存分配器使用的时间。
  • 您是在 64 位运行吗?我想知道答案是否与扩展地址空间有关...

标签: c linux performance unix compiler-construction


【解决方案1】:

您还应该使用malloc 而不是calloc 进行测试。 calloc 所做的一件事是用零填充分配的内存。

我相信在您的情况下,当您最后一个 calloc arr1 然后分配给它时,它已经出现在高速缓存内存中,因为它是最后一个分配并填充零的内存。当您首先calloc arr1 和第二个arr2 时,arr2 的零填充会将arr1 推出缓存。

【讨论】:

  • 使用malloc 会增加所用时间——两个循环现在都消耗约0.9 秒。但我得到了你的答案,谢谢!
  • 嗯,但为什么数字会互换?按照这个逻辑,如果第二个使用数组calloced,第一个循环应该运行得更快。但是,如果它首先使用数组 I calloc,它应该将较热的数组从缓存中驱出(第二个 calloced),并且两个循环都应该运行缓慢,但事实并非如此。
  • @SCombinator:真奇怪。你没有在你的问题中具体说明发生了什么。这与我的想法相反。在这里疯狂猜测:零写入被流式传输到 RAM 并重写该数据需要 CPU 对内存控制器执行一些复杂的操作。
  • 我一直认为calloc 足够聪明,可以知道 mmap 返回已经归零的内存。
  • @PerJohansson:确实如此,但操作系统必须将这些页面归零,因此速度仍然存在。除非机器有某种 DMA 引擎,它可以“免费”将 RAM 填零。
【解决方案2】:

我猜我本可以写得更多或更少,尤其是少即是多。

原因可能因系统而异。然而;对于 clib:

每个操作所用的总时间是相反的;如果你时间 calloc + 迭代。

即:

Calloc arr1 : 0.494992654
Calloc arr2 : 0.000021250
Itr arr1    : 0.430646035
Itr arr2    : 0.790992411
Sum arr1    : 0.925638689
Sum arr2    : 0.791013661

Calloc arr1 : 0.503130736
Calloc arr2 : 0.000025906
Itr arr1    : 0.427719162
Itr arr2    : 0.809686047
Sum arr1    : 0.930849898
Sum arr2    : 0.809711953

第一个calloc 随后malloc 的执行时间更长 第二。在任何calloc 等之前调用malloc(0) 等以平衡时间 用于malloc 类似进程中的调用(解释如下)。一个可以 但是,如果一个人排队做几个,这些调用的时间会略有下降。

然而,迭代时间会变平。

简而言之;使用的总系统时间是最先分配的。 然而,这是在进程的限制中无法逃脱的开销。

正在进行大量维护。快速了解一些案例:


页面短

当一个进程请求内存时,它会得到一个虚拟地址范围。这个范围 通过页表转换为物理内存。如果一个页面按字节翻译 字节我们会很快得到巨大的页表。作为一个,这就是为什么 内存范围以块或页面的形式提供。页面大小为系统 依赖。该架构还可以提供各种页面大小。

如果我们查看上述代码的执行并添加一些来自/proc/PID/stat 的读取 我们看到了这一点(特别是注释 RSS):

PID Stat {
  PID          : 4830         Process ID
  MINFLT       : 214          Minor faults, (no page memory read)
  UTIME        : 0            Time user mode
  STIME        : 0            Time kernel mode
  VSIZE        : 2039808      Virtual memory size, bytes
  RSS          : 73           Resident Set Size, Number of pages in real memory
} : Init

PID Stat {
  PID          : 4830         Process ID
  MINFLT       : 51504        Minor faults, (no page memory read)
  UTIME        : 4            Time user mode
  STIME        : 33           Time kernel mode
  VSIZE        : 212135936    Virtual memory size, bytes
  RSS          : 51420        Resident Set Size, Number of pages in real memory
} : Post calloc arr1

PID Stat {
  PID          : 4830         Process ID
  MINFLT       : 51515        Minor faults, (no page memory read)
  UTIME        : 4            Time user mode
  STIME        : 33           Time kernel mode
  VSIZE        : 422092800    Virtual memory size, bytes
  RSS          : 51428        Resident Set Size, Number of pages in real memory
} : Post calloc arr2

PID Stat {
  PID          : 4830         Process ID
  MINFLT       : 51516        Minor faults, (no page memory read)
  UTIME        : 36           Time user mode
  STIME        : 33           Time kernel mode
  VSIZE        : 422092800    Virtual memory size, bytes
  RSS          : 51431        Resident Set Size, Number of pages in real memory
} : Post iteration arr1

PID Stat {
  PID          : 4830         Process ID
  MINFLT       : 102775       Minor faults, (no page memory read)
  UTIME        : 68           Time user mode
  STIME        : 58           Time kernel mode
  VSIZE        : 422092800    Virtual memory size, bytes
  RSS          : 102646       Resident Set Size, Number of pages in real memory
} : Post iteration arr2

PID Stat {
  PID          : 4830         Process ID
  MINFLT       : 102776       Minor faults, (no page memory read)
  UTIME        : 68           Time user mode
  STIME        : 69           Time kernel mode
  VSIZE        : 2179072      Virtual memory size, bytes
  RSS          : 171          Resident Set Size, Number of pages in real memory
} : Post free()

我们可以看到实际分配在内存中的页面被推迟到arr2等待 页面请求;一直持续到迭代开始。如果我们在前面添加malloc(0) callocarr1 我们可以注册这两个数组都没有在物理上分配 迭代前的内存。


由于可能不使用页面,因此根据请求进行映射会更有效。 这就是为什么当这个过程,即做一个calloc足够数量的页面 是保留的,但不一定实际分配在实际内存中。

当引用地址时,会查询页表。如果地址是 在未分配的页面中,系统会处理 page fault 并且该页面 随后被分配。分配页面的总和称为驻留 设置大小 (RSS)。

我们可以通过迭代(触摸)即 1/4 来对我们的数组进行实验。 在这里,我还在任何calloc 之前添加了malloc(0)

Pre iteration 1/4:
RSS          : 171              Resident Set Size, Number of pages in real meory

for (i = 0; i < SIZE / 4; ++i)
    arr1[i] = 0;

Post iteration 1/4:
RSS          : 12967            Resident Set Size, Number of pages in real meory

Post iteration 1/1:
RSS          : 51134            Resident Set Size, Number of pages in real meory

为了进一步加快速度,大多数系统还会缓存 N 个最新的 翻译后备缓冲区 (TLB) 中的页表条目。


brk,映射

当进程(c|m|…)alloc 堆的上限扩展为 brk()sbrk()。这些系统调用代价高昂,需要弥补 这个malloc 将多个较小的调用收集到一个更大的 brk() 中。

这也会影响free(),因为它是负面的brk() 也是资源昂贵 它们被收集起来并作为一个更大的操作执行。


对于巨大的请求;即,就像您的代码中的那个一样,malloc() 使用 mmap()The threshold 对此,可通过mallopt() 进行配置,是受过教育的 价值

我们可以通过修改代码中的SIZE 来获得乐趣。如果我们利用 malloc.h 并使用,

struct mallinfo minf = mallinfo();

(不,不是 milf),我们可以展示这个(注意 ArenaHblkhd,...):

Initial:

mallinfo {
  Arena   :         0 (Bytes of memory allocated with sbrk by malloc)
  Ordblks :         1 (Number of chunks not in use)
  Hblks   :         0 (Number of chunks allocated with mmap)
  Hblkhd  :         0 (Bytes allocated with mmap)
  Uordblks:         0 (Memory occupied by chunks handed out by malloc)
  Fordblks:         0 (Memory occupied by free chunks)
  Keepcost:         0 (Size of the top-most releasable chunk)
} : Initial

MAX = ((128 * 1024) / sizeof(int)) 

mallinfo {
  Arena   :         0 (Bytes of memory allocated with sbrk by malloc)
  Ordblks :         1 (Number of chunks not in use)
  Hblks   :         1 (Number of chunks allocated with mmap)
  Hblkhd  :    135168 (Bytes allocated with mmap)
  Uordblks:         0 (Memory occupied by chunks handed out by malloc)
  Fordblks:         0 (Memory occupied by free chunks)
  Keepcost:         0 (Size of the top-most releasable chunk)
} : After malloc arr1

mallinfo {
  Arena   :         0 (Bytes of memory allocated with sbrk by malloc)
  Ordblks :         1 (Number of chunks not in use)
  Hblks   :         2 (Number of chunks allocated with mmap)
  Hblkhd  :    270336 (Bytes allocated with mmap)
  Uordblks:         0 (Memory occupied by chunks handed out by malloc)
  Fordblks:         0 (Memory occupied by free chunks)
  Keepcost:         0 (Size of the top-most releasable chunk)
} : After malloc arr2

然后我们从MAX 中减去sizeof(int) 得到:

mallinfo {
  Arena   :    266240 (Bytes of memory allocated with sbrk by malloc)
  Ordblks :         1 (Number of chunks not in use)
  Hblks   :         0 (Number of chunks allocated with mmap)
  Hblkhd  :         0 (Bytes allocated with mmap)
  Uordblks:    131064 (Memory occupied by chunks handed out by malloc)
  Fordblks:    135176 (Memory occupied by free chunks)
  Keepcost:    135176 (Size of the top-most releasable chunk)
} : After malloc arr1

mallinfo {
  Arena   :    266240 (Bytes of memory allocated with sbrk by malloc)
  Ordblks :         1 (Number of chunks not in use)
  Hblks   :         0 (Number of chunks allocated with mmap)
  Hblkhd  :         0 (Bytes allocated with mmap)
  Uordblks:    262128 (Memory occupied by chunks handed out by malloc)
  Fordblks:      4112 (Memory occupied by free chunks)
  Keepcost:      4112 (Size of the top-most releasable chunk)
} : After malloc arr2

我们注册该系统的工作方式与宣传的一样。如果分配的大小是 低于阈值sbrk 被使用并且内存由malloc 在内部处理, 否则使用mmap

这种结构也有助于防止内存碎片等。


要点是malloc 系列针对一般用途进行了优化。然而 mmap 限制可以修改以满足特殊需要。

Note this(并在 100+ 行以下)当/如果修改 mmap 阈值。 .

如果我们填充(触摸)arr1 和 arr2 的每一页,可以进一步观察到这一点 在我们进行计时之前:

Touch pages … (Here with page size of 4 kB)

for (i = 0; i < SIZE; i += 4096 / sizeof(int)) {
    arr1[i] = 0;
    arr2[i] = 0;
}

Itr arr1    : 0.312462317
CPU arr1    : 0.32

Itr arr2    : 0.312869158
CPU arr2    : 0.31

另见:


子注释:

那么,CPU 知道物理地址吗?不。

在记忆的世界里,很多事情都需要解决 ;)。核心硬件 这是内存管理单元 (MMU)。要么作为集成的一部分 CPU 或外部芯片。

操作系统在启动时配置 MMU 并定义各种访问权限 区域(只读、读写等),从而提供一定程度的安全性。

我们凡人看到的地址是CPU使用的逻辑地址。这 MMU 将其转换为物理地址

CPU 的地址由两部分组成:页地址和偏移量。 [PAGE_ADDRESS.OFFSET]

而获取物理地址的过程我们可以有这样的:

.-----.                          .--------------.
| CPU > --- Request page 2 ----> | MMU          |
+-----+                          | Pg 2 == Pg 4 |
      |                          +------v-------+
      +--Request offset 1 -+            |
                           |    (Logical page 2 EQ Physical page 4)
[ ... ]     __             |            |
[ OFFSET 0 ]  |            |            |
[ OFFSET 1 ]  |            |            |
[ OFFSET 2 ]  |            |            |     
[ OFFSET 3 ]  +--- Page 3  |            |
[ OFFSET 4 ]  |            |            |
[ OFFSET 5 ]  |            |            |
[ OFFSET 6 ]__| ___________|____________+
[ OFFSET 0 ]  |            |
[ OFFSET 1 ]  | ...........+
[ OFFSET 2 ]  |
[ OFFSET 3 ]  +--- Page 4
[ OFFSET 4 ]  |
[ OFFSET 5 ]  |
[ OFFSET 6 ]__|
[ ... ]

CPU 的逻辑地址空间与地址长度直接相关。一种 32 位地址处理器的逻辑地址空间为 232 个字节。 物理地址空间是系统可以承受的内存量。

还有碎片化内存的处理,重新对齐等

这将我们带入了交换文件的世界。如果进程请求更多内存 然后是物理可用的;一页或几页其他进程是 由请求进程转移到磁盘/交换及其页面“被盗”。 MMU 对此进行跟踪;因此CPU不必担心在哪里 内存实际上是定位的。


这进一步将我们带入脏记忆。

如果我们从 /proc/[pid]/smaps 打印一些信息,更具体的范围 在我们的数组中,我们得到如下内容:

Start:
b76f3000-b76f5000
Private_Dirty:         8 kB

Post calloc arr1:
aaeb8000-b76f5000
Private_Dirty:        12 kB

Post calloc arr2:
9e67c000-b76f5000
Private_Dirty:        20 kB

Post iterate 1/4 arr1
9e67b000-b76f5000
Private_Dirty:     51280 kB

Post iterate arr1:
9e67a000-b76f5000
Private_Dirty:    205060 kB

Post iterate arr2:
9e679000-b76f5000
Private_Dirty:    410096 kB

Post free:
9e679000-9e67d000
Private_Dirty:        16 kB
b76f2000-b76f5000
Private_Dirty:        12 kB

创建虚拟页面时,系统通常会清除其中的 脏位 页面。
当 CPU 写入该页面的一部分时,脏位被设置;因此当 交换了脏位的页面被写入,干净的页面被跳过。


【讨论】:

    【解决方案3】:

    简答

    calloc 第一次被调用时,它明确地将内存清零。下一次调用它时,它假定从mmap 返回的内存已经清零。

    详情

    为了得出这个结论,我检查了以下一些事情,如果你愿意,你可以自己尝试一下:

    1. 在您的第一个 ALLOC 呼叫之前插入一个 calloc 呼叫。你会看到在这之后时间A和时间B的时间是一样的。

    2. 使用clock() 函数检查每个ALLOC 调用需要多长时间。在他们都使用calloc 的情况下,您会看到第一个调用比第二个调用花费的时间长得多。

    3. 使用time 来计时calloc 版本和USE_MMAP 版本的执行时间。当我这样做时,我发现USE_MMAP 的执行时间始终略短。

    4. 我使用strace -tt -T 运行,它显示了进行系统调用的时间和花费了多长时间。以下是部分输出:

    Strace 输出:

    21:29:06.127536 mmap(NULL, 2000015360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fff806fd000 <0.000014>
    21:29:07.778442 mmap(NULL, 2000015360, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fff093a0000 <0.000021>
    21:29:07.778563 times({tms_utime=63, tms_stime=102, tms_cutime=0, tms_cstime=0}) = 4324241005 <0.000011>
    

    您可以看到第一个mmap 调用花费了0.000014 秒,但在下一个系统调用之前大约过了1.5 秒。然后第二个mmap 调用花费了0.000021 秒,然后是几百微秒后times 调用。

    我还使用gdb 逐步执行了部分应用程序,发现第一次调用calloc 导致多次调用memset,而第二次调用calloc 并没有调用@987654345 @。有兴趣的可以看callochere的源码(找__libc_calloc)。至于为什么calloc 在第一次调用时会调用memset 而不是后续调用,我不知道。但我相当有信心这可以解释您所询问的行为。

    至于为什么被归零的数组memset 提高了性能,我猜这是因为值被加载到 TLB 而不是缓存中,因为它是一个非常大的数组。无论您询问的性能差异的具体原因是两个calloc 调用在执行时的行为不同。

    【讨论】:

    • 我原本以为是和缓存有关的行为,但我现在猜测是TLB或其他与虚拟内存和页面分配有关的东西。分配的内存量是如此之大,以至于我认为它不会保留在缓存中。无论如何,我相信性能差异与内存是否被calloc 明确归零有关。
    • 略读源代码我认为第二个 calloc 可能不需要将任何 mem 归零的原因是因为例程知道操作系统新分配给进程的 mem 已经归零了。
    • 确实如此,但在此示例中的第一个 calloc 调用也是如此。所以真的他们都不应该打电话给memset
    【解决方案4】:

    这只是进程内存映像何时扩展一页的问题。

    【讨论】:

    • 这与堆栈无关——这里所有的内存都来自堆,来自mmap
    • 已修复。谢谢。我实际上将callocalloca 混淆了!
    • 我不明白——两个分配都是偶数页。
    • 当你调用分配函数时,实际的分配还没有发生。它发生在第一次使用时。分配就像存款——银行将钱记录在你的账户中,但还没有具体的钱属于你。第一次使用就像取款一样,银行必须实际去找一些钱给你。
    • 最佳答案,非常简洁!
    【解决方案5】:

    总结在分析分配数组所需的时间时解释了时间差。最后分配的 calloc 只需要更多时间,而另一个(或使用 mmap 时全部)几乎没有时间。第一次访问时,内存中的实际分配可能会延迟。

    我对 Linux 内存分配的内部了解不够。但是我稍微修改了您的脚本:我添加了第三个数组和每个数组操作的一些额外迭代。并且我已经考虑到了Old Pro的说法,即没有考虑分配数组的时间。

    结论:使用 calloc 比使用 mmap 分配时间更长(mmap virtualy 分配内存时不使用时间,可能稍后在第一次访问时推迟),并且使用我的程序最终使用几乎没有区别用于整个程序执行的 mmap 或 calloc。

    无论如何,首先要说明的是,内存分配都发生在内存映射区域而不是堆中。为了验证这一点,我添加了一个快速的 n'dirty pause 以便您可以检查进程的内存映射 (/proc//maps)

    现在你的问题是,最后分配的带有 calloc 的数组似乎真的分配在内存中(没有推迟)。由于 arr1 和 arr2 现在的行为完全相同(第一次迭代很慢,后续迭代更快)。 Arr3 在第一次迭代中更快,因为内存分配得更早。使用 A 宏时,arr1 会从中受益。我的猜测是内核已经为最后一个 calloc 预先分配了内存中的数组。为什么?我不知道...我也只用一个数组对其进行了测试(所以我删除了所有出现的 arr2 和 arr3),然后我对 arr1 的所有 10 次迭代都有相同的时间(大致)。

    malloc 和 mmap 的行为相同(结果未在下面显示),第一次迭代很慢,后续迭代对所有 3 个数组都更快。

    注意:所有结果在各种 gcc 优化标志(-O0 到 -O3)上都是一致的,因此看起来行为的根源并非源自某种 gcc 优化。

    注意 2:在 Ubuntu Precise Pangolin(内核 3.2)上测试运行,使用 GCC 4.6.3

    #include <stdlib.h>
    #include <stdio.h>
    #include <sys/mman.h>
    
    #include <time.h>
    
    #define SIZE 500002816
    #define ITERATION 10
    
    #if defined(USE_MMAP)
    #  define ALLOC(a, b) (mmap(NULL, a * b, PROT_READ | PROT_WRITE,  \
                              MAP_PRIVATE | MAP_ANONYMOUS, -1, 0))
    #elif defined(USE_MALLOC)
    #  define ALLOC(a, b) (malloc(b * a))
    #elif defined(USE_CALLOC)
    #  define ALLOC calloc
    #else
    #  error "No alloc routine specified"
    #endif
    
    int main() {
      clock_t start, finish, gstart, gfinish;
      start = clock();
      gstart = start;
    #ifdef A
      unsigned int *arr1 = ALLOC(sizeof(unsigned int), SIZE);
      unsigned int *arr2 = ALLOC(sizeof(unsigned int), SIZE);
      unsigned int *arr3 = ALLOC(sizeof(unsigned int), SIZE);
    #else
      unsigned int *arr3 = ALLOC(sizeof(unsigned int), SIZE);
      unsigned int *arr2 = ALLOC(sizeof(unsigned int), SIZE);
      unsigned int *arr1 = ALLOC(sizeof(unsigned int), SIZE);
    #endif
      finish = clock();
      unsigned int i, j;
      double intermed, finalres;
    
      intermed = ((double)(finish - start))/CLOCKS_PER_SEC;
      printf("Time to create: %.2f\n", intermed);
    
      printf("arr1 addr: %p\narr2 addr: %p\narr3 addr: %p\n", arr1, arr2, arr3);
    
      finalres = 0;
      for (j = 0; j < ITERATION; j++)
      {
        start = clock();
        {
          for (i = 0; i < SIZE; i++)
            arr1[i] = (i + 13) * 5;
        }
        finish = clock();
    
        intermed = ((double)(finish - start))/CLOCKS_PER_SEC;
        finalres += intermed;
        printf("Time A: %.2f\n", intermed);
      }
    
      printf("Time A (average): %.2f\n", finalres/ITERATION);
    
    
      finalres = 0;
      for (j = 0; j < ITERATION; j++)
      {
        start = clock();
        {
          for (i = 0; i < SIZE; i++)
            arr2[i] = (i + 13) * 5;
        }
        finish = clock();
    
        intermed = ((double)(finish - start))/CLOCKS_PER_SEC;
        finalres += intermed;
        printf("Time B: %.2f\n", intermed);
      }
    
      printf("Time B (average): %.2f\n", finalres/ITERATION);
    
    
      finalres = 0;
      for (j = 0; j < ITERATION; j++)
      {
        start = clock();
        {
          for (i = 0; i < SIZE; i++)
            arr3[i] = (i + 13) * 5;
        }
        finish = clock();
    
        intermed = ((double)(finish - start))/CLOCKS_PER_SEC;
        finalres += intermed;
        printf("Time C: %.2f\n", intermed);
      }
    
      printf("Time C (average): %.2f\n", finalres/ITERATION);
    
      gfinish = clock();
    
      intermed = ((double)(gfinish - gstart))/CLOCKS_PER_SEC;
      printf("Global Time: %.2f\n", intermed);
    
      return 0;
    }
    

    结果:

    使用 USE_CALLOC

    Time to create: 0.13
    arr1 addr: 0x7fabcb4a6000
    arr2 addr: 0x7fabe917d000
    arr3 addr: 0x7fac06e54000
    Time A: 0.67
    Time A: 0.48
    ...
    Time A: 0.47
    Time A (average): 0.48
    Time B: 0.63
    Time B: 0.47
    ...
    Time B: 0.48
    Time B (average): 0.48
    Time C: 0.45
    ...
    Time C: 0.46
    Time C (average): 0.46
    

    使用 USE_CALLOC 和 A

    Time to create: 0.13
    arr1 addr: 0x7fc2fa206010
    arr2 addr: 0xx7fc2dc52e010
    arr3 addr: 0x7fc2be856010
    Time A: 0.44
    ...
    Time A: 0.43
    Time A (average): 0.45
    Time B: 0.65
    Time B: 0.47
    ...
    Time B: 0.46
    Time B (average): 0.48
    Time C: 0.65
    Time C: 0.48
    ...
    Time C: 0.45
    Time C (average): 0.48
    

    使用 USE_MMAP

    Time to create: 0.0
    arr1 addr: 0x7fe6332b7000
    arr2 addr: 0x7fe650f8e000
    arr3 addr: 0x7fe66ec65000
    Time A: 0.55
    Time A: 0.48
    ...
    Time A: 0.45
    Time A (average): 0.49
    Time B: 0.54
    Time B: 0.46
    ...
    Time B: 0.49
    Time B (average): 0.50
    Time C: 0.57
    ...
    Time C: 0.40
    Time C (average): 0.43
    

    【讨论】:

    • 我尝试设置火焰图来查看时间丢失的地方。但直到现在我都没有成功,主要是因为我的虚拟机内存不足以及对 SystemTap 或 Perf 缺乏了解。见文章:dtrace.org/blogs/brendan/2012/03/17/…
    猜你喜欢
    • 2021-09-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多