猜猜我本可以写更多或更少,尤其是少即是多。
原因可能因系统而异。然而; 对于 clib:
每个操作使用的总时间是相反的;如果你计时calloc
+ 迭代。
IE:
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
具有比第二个更长的执行时间。malloc(0)
在任何等之前的调用会平衡同一进程中calloc
用于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)
before
calloc
,arr1
我们可以注册在迭代之前两个数组都没有分配到物理内存中。
由于可能不使用页面,因此根据请求进行映射会更有效。这就是为什么当进程 ie 执行calloc
足够数量的页面时被保留,但不一定实际分配在实际内存中。
当引用地址时,会查询页表。如果地址位于未分配的页面中,则系统会出现页面错误,并且随后会分配该页面。分配页面的总和称为驻留集大小(RSS)。
我们可以通过迭代(触摸)即 1/4 来对我们的数组进行实验。这里我还添加malloc(0)
了 any calloc
。
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
为了进一步加快速度,大多数系统还在转换后备缓冲区(TLB) 中缓存 N 个最近的页表条目。
brk, 地图
当一个进程(c|m|…)alloc
堆的上限扩展为
brk()
或时sbrk()
。这些系统调用很昂贵,为了弥补这一点,malloc
将多个较小的调用收集到一个更大的 brk() 中。
这也会free()
产生负面影响,brk()
而且资源昂贵,它们被收集并作为更大的操作执行。
对于巨大的要求;即像您的代码中的那个一样,malloc()
使用mmap()
.
此阈值可由 配置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
然后我们减去并sizeof(int)
得到MAX
:
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
,可以修改限制以满足特殊需要。
当/如果修改 mmap 阈值时,请注意这一点(以及 100 多行)。.
如果我们在计时之前填充(触摸)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 位地址处理器具有 2个 32字节的逻辑地址空间。物理地址空间是系统可以承受的内存量。
还有碎片内存的处理,重新对齐等。
这将我们带入了交换文件的世界。如果一个进程请求更多内存,那么它是物理可用的;其他进程的一个或多个页面被转移到磁盘/交换,并且它们的页面被请求进程“窃取”。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 写入该页面的一部分时,脏位被设置;因此,当交换带有脏位的页面时,会跳过干净的页面。