为什么逐项赋值没有memcpy() 快?

为什么逐项赋值没有memcpy() 快?

C措辞程序员都是乐于思考的,在调用 memcpy() 函数实现内存拷贝时,每每会思考 memcpy() 函数的实现办法。
在一些程序员看来,memcpy() 无非便是下面这样的逐项拷贝:

int i;for(i=0; i<N; i++) pDest++ = pSrc++;

考虑到 memcpy() 函数可以吸收任意类型的源内存段指针和目标内存段指针,用C措辞来描述便是 memcpy() 函数吸收的源内存段指针和目标内存段指针都是 void 指针,因此可能还要多一步指针类型转换:

php有memcpy函数吗C说话法式员应看重效力memcpy函数这么高效应用了什么技能 PHP

void my_memcpy(void dst, void src, unsigned int bytes){ unsigned char b_dst = (unsigned char)dst; unsigned char b_src = (unsigned char)src; for (int i = 0; i < bytes; ++i) b_dst++ = b_src++;}

可能还要多一步指针类型转换

这里转换为 unsigned char 指针只仅作为示例,读者当然可以将其转换为其他类型指针。

乐于动手的C措辞程序员一定自己考试测验实现过自己的 memcpy() 函数,如果读者考试测验过自己的实现,在性能测试中一定能够创造,上述实现的效率并没有 memcpy() 库函数的效率高,这是为什么呢?

memcpy() 函数利用的技巧

既然逐字节赋值拷贝的 my_memcpy() 效率比不上 memcpy() 函数,那么 memcpy() 函数一定利用了某些技巧,到底是什么技巧呢?

前文先容的 my_memcpy() 实现是将吸收到的指针转换成 char 指针,逐字节拷贝的。
但是 memcpy() 函数一样平常不该用字节指针,而是利用字指针。

看过我之前文章的读者该当明白,CPU 在处理数据时,如果数据严格按照数据总线宽度对齐,那么CPU才能最大效率处理数据。

事实上,memcpy() 函数的实现常日利用 SIMD 指令编写,这使得它能够一次操作 128 位数据。
关于 SIMD 指令,往后有机会再谈论。
现在读者只需知道,memcpy() 函数一次可以拷贝 16 字节的数据就可以了,这比一次只拷贝 1 字节的数据效率高多了。

改进 my_memcpy() 函数,提升效率的方向

C措辞程序员在实际拷贝数据时,要拷贝的数据长度不一定是 16 字节的整数倍,以是 memcpy() 的实现要比 my_memcpy() 的实现繁芜得多。

memcpy() 的实现要比 my_memcpy() 的实现繁芜得多

my_memcpy() 的第一个改进是在内存时按照本机字宽度( 32位机器一样平常是4字节,64位机器一样平常是8字节)对齐要拷贝的数据,并且利用字指针而不是字节指针拷贝数据,干系的C措辞代码如下:

void aligned_memory_copy(void dst, void src, unsigned int bytes){ unsigned char b_dst = (unsigned char)dst; unsigned char b_src = (unsigned char)src; // Copy bytes to align source pointer while ((b_src & 0x3) != 0) { b_dst++ = b_src++; bytes--; } unsigned int w_dst = (unsigned int)b_dst; unsigned int w_src = (unsigned int)b_src; while (bytes >= 4) { w_dst++ = w_src++; bytes -= 4; } // Copy trailing bytes if (bytes > 0) { b_dst = (unsigned char)w_dst; b_src = (unsigned char)w_src; while (bytes > 0) { b_dst++ = b_src++; bytes--; } }}

利用字指针而不是字节指针拷贝数据

源内存段指针或者目标内存段指针是否精确对齐,在不同架构的机器年夜将实行不同的操作。
例如,在 XScale 处理器上,通过对齐目标内存段指针,在实际性能测试中,我得到了更高的内存拷贝效率。

若想进一步提升内存拷贝的效率,可以将一些C措辞代码中的循环展开,便于提高 CPU 缓存的命中率。
不过这种办法带来的效率提升会因体系架构的不同而不同,由于 CPU 加载和存储数据的办法可能是不同的。

还有一种提升效率的办法,即手动编写 CPU 加载和存储指令,以掌握数据吞吐量。
不过这种办法须要借助汇编指令实现,C措辞暂时还没有能力实现这样的风雅操作。

小结

随意马虎看出,在C措辞程序开拓中,大略的内存拷贝也并不大略,我们有着相称多的优化空间。
C措辞程序的灵魂之一便是高效率,而C措辞本身供应的都是最根本的操作,因此它是一种相称重视设计的程序措辞,作为C措辞程序员,我们在设计某个方法时,该当总是想清楚如何才能写出更高效的程序。

点个关注,再点个赞再走吧

欢迎在评论区一起谈论,质疑。
文章都是手打原创,每天最浅近的先容C措辞、linux等嵌入式开拓,喜好我的文章就关注一波吧,可以看到最新更新和之前的文章哦。

未经容许,禁止转载。