先上原代码:
void *memset (void *dstpp, int c, size_t len)
{
long int dstp = (long int) dstpp;
if (len >= 8)
{
size_t xlen;
op_t cccc;
cccc = (unsigned char) c;
cccc |= cccc << 8;
cccc |= cccc << 16;
if (OPSIZ > 4)
/* Do the shift in two steps to avoid warning if long has 32 bits. */
cccc |= (cccc << 16) << 16;
/* There are at least some bytes to set.
No need to test for LEN == 0 in this alignment loop. */
while (dstp % OPSIZ != 0)
{
((byte *) dstp)[0] = c;
dstp += 1;
len -= 1;
}
/* Write 8 `op_t' per iteration until less than 8 `op_t' remain. */
xlen = len / (OPSIZ * 8);
while (xlen > 0)
{
((op_t *) dstp)[0] = cccc;
((op_t *) dstp)[1] = cccc;
((op_t *) dstp)[2] = cccc;
((op_t *) dstp)[3] = cccc;
((op_t *) dstp)[4] = cccc;
((op_t *) dstp)[5] = cccc;
((op_t *) dstp)[6] = cccc;
((op_t *) dstp)[7] = cccc;
dstp += 8 * OPSIZ;
xlen -= 1;
}
len %= OPSIZ * 8;
/* Write 1 `op_t' per iteration until less than OPSIZ bytes remain. */
xlen = len / OPSIZ;
while (xlen > 0)
{
((op_t *) dstp)[0] = cccc;
dstp += OPSIZ;
xlen -= 1;
}
len %= OPSIZ;
}
/* Write the last few bytes. */
while (len > 0)
{
((byte *) dstp)[0] = c;
dstp += 1;
len -= 1;
}
return dstpp;
}
灵魂发问:为什么有4次while循环,各有什么作用?
以下为个人理解,不正之处还请指出:
-
数据总线决定了一次性最多可访问数据的位数(常见的计算机是32位或64位),位数等于[字]长,也就是说机器一次性最多可访问一个[字] 长,因此,在批量内存访问中,按[字]访问最高效,我们看到这段代码也是尽可能按[字]写入的;
1)什么是内存对齐?将要操作的内存地址指针偏移到能整除[字]长的位置即可;
2)为什么要内存对齐?例如,非对齐情况下读取一个字长的内存,计算机需分别读取目标内存左右两个字的内容组合成我们所需的字,这就增加了额外操作(这里的额外操作是硬件层面的,汇编代码就一句);而对齐之后,计算机只需读取目标字的内容即可,无需再组合,因此寻址效率更高, 进而提高内存操作指令的执行效率; -
第一个while是准备工作,操作目标为未对齐的内存部分,执行完这个while之后内存就对齐了,之后第二、第三个while执行效率会更高;
-
第二个while是主循环,操作目标为已进行字节对齐的大块内存,大部分内存赋值操作在这里进行,但为什么是8次赋值为一个循环周期?也就是8倍内存单元为一块?
1)为了尽可能减少边界判断和辅助变量赋值操作次数,以提高内存赋值操作在循环周期里的比重;
2)8倍是经典值,符合一般申请的内存大小分布规律,大于8或小于8都会减少这个while的执行率,说白了就是要提高这个while被执行的几率; -
第三个while是次循环,操作目标为已进行字节对齐但又不足8倍对齐单元的内存部分,每次为一个对齐单元赋值;
-
第四个while是收尾工作,操作目标为余下的不足一个对齐单元的部分,这部分只能挨个字节赋值了;
底层函数之所以执行效率高,除了对计算机原理有深刻理解,设计也是很巧妙啊
orz
网友评论