我猜我本可以写得更多或更少,尤其是少即是多。
原因可能因系统而异。然而;对于 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)
calloc 的 arr1 我们可以注册这两个数组都没有在物理上分配
迭代前的内存。
由于可能不使用页面,因此根据请求进行映射会更有效。
这就是为什么当这个过程,即做一个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),我们可以展示这个(注意 Arena 和 Hblkhd,...):
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 写入该页面的一部分时,脏位被设置;因此当
交换了脏位的页面被写入,干净的页面被跳过。