0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

【性能优化】memcpy函数有没有更高效的拷贝实现方法?

嵌入式物联网开发 来源:嵌入式物联网开发 作者:嵌入式物联网开发 2022-12-07 08:59 次阅读

C语言经典面试题】memcpy函数有没有更高效的拷贝实现方法?

我相信大部分初中级C程序员在面试的过程中,可能都被问过关于memcpy函数的问题,甚至需要手撕memcpy。本文从另一个角度带你领悟一下memcpy的面试题,你可以看看是否能接得住?

1 写在前面2 源码实现2.1 函数申明2.2 简单的功能实现2.3 满足大数据量拷贝的功能实现3 源码测试4 小小总结5 更多分享

1 写在前面

假如你遇到下面的面试题,你会怎么做?题目大意如下:

请参考标准C库对memcpy的申明定义,使用C语言的语法实现其基本功能,并尽量保证它在拷贝大数据(KK级别)的时候,有比较好的性能表现。

2 源码实现

2.1 函数申明

通过查看man帮助,我们可以知道memcpy函数的功能及其简要申明。

NAME
        memcpy - copy memory area
 ​
 SYNOPSIS
        #include

英文翻译过来就是说,memcpy实现的就是内存拷贝,其是按字节进行拷贝,同时还可能会存在内存区域重合的情况。

2.2 简单的功能实现

根据功能需求,以下是我的一个简单实现源码,仅供参考:

char *my_memcpy(char* dest, const char *src, size_t len)
 {
     assert(dest && src && (len > 0));
 
 if (dest == src) {
 ;
 } else {
         char *p = dest;
 size_t i;
         for (i = 0; i < len; i++) {
             *p++ = *src++;
 }
     } 
 ​
     return dest;
 }

但是,这段代码的缺陷也比较明显,但数据量过大的时,即len很大时,整一个拷贝耗时将会非常不理想。那么如果考虑性能问题,又该如何实现它呢?

2.3 满足大数据量拷贝的功能实现

下面给出一个参考实现:

/* Nonzero if either X or Y is not aligned on a "long" boundary.  */
 #define UNALIGNED(X, Y) \\
 (((long)X & (sizeof(long) - 1)) | ((long)Y & (sizeof(long) - 1)))
 ​
 /* How many bytes are copied each iteration of the 4X unrolled loop.  */
 #define BIGBLOCKSIZE    (sizeof(long) << 2)/* How many bytes are copied each iteration of the word copy loop.  */
 #define LITTLEBLOCKSIZE (sizeof(long))/* Threshhold for punting to the byte copier.  */
 #define TOO_SMALL(LEN)  ((LEN) < BIGBLOCKSIZE)char *my_memcopy_super(char* dest0, const char *src0, size_t len0)
 {
     assert(dest0 && src0 && (len0 > 0));
 
 char *dest = dest0;
 const char *src = src0;
 long *aligned_dest;
 const long *aligned_src;
 ​
 /* If the size is small, or either SRC or DST is unaligned,
    then punt into the byte copy loop.  This should be rare.  */
 if (!TOO_SMALL(len0) && !UNALIGNED(src, dest)) {
 aligned_dest = (long *)dest;
 aligned_src = (long *)src;
 ​
 /* Copy 4X long words at a time if possible.  */
 while (len0 >= BIGBLOCKSIZE) {
 *aligned_dest++ = *aligned_src++;
 *aligned_dest++ = *aligned_src++;
 *aligned_dest++ = *aligned_src++;
 *aligned_dest++ = *aligned_src++;
 len0 -= BIGBLOCKSIZE;
 }
 ​
 /* Copy one long word at a time if possible.  */
 while (len0 >= LITTLEBLOCKSIZE) {
 *aligned_dest++ = *aligned_src++;
 len0 -= LITTLEBLOCKSIZE;
 }
 ​
 /* Pick up any residual with a byte copier.  */
 dest = (char *)aligned_dest;
 src = (char *)aligned_src;
 }
 ​
 while (len0--)
 *dest++ = *src++;
 ​
 return dest0;
 }
 ​

我们可以看到,里面做了对齐的判断,还有数据量长度的判断;通过充分利用机器的操作性能,从而提升memcpy拷贝的效率。

3 源码测试

**简单的测试代码如下,目的就是测试在拷贝 **1KB数据和10MB数据 时,标准C的memcpy、自定义的memcpy_normal、以及自定义的memcpy_super直接的性能差异:

#include 
 #include static void get_rand_bytes(unsigned char *data, int len)
 {
     int a;
     int i;
 ​
     srand((unsigned)time(NULL)); //种下随机种子
     for (i = 0; i < len; i++) {
         data[i] = rand() % 255; //取随机数,并保证数在0-255之间
         //printf("%02X ", data[i]);
     }  
 }
 ​
 static int get_cur_time_us(void)
 {
     struct timeval tv;gettimeofday(&tv, NULL);  //使用gettimeofday获取当前系统时间return (tv.tv_sec * 1000 * 1000 + tv.tv_usec); //利用struct timeval结构体将时间转换为ms
 }
 ​
 #define ARRAY_SIZE(n)  sizeof(n) / sizeof(n[0])int main(void)
 {
 int size_list[] = {
 1024 * 1024 * 10,  // 10MB
 1024 * 1024 * 1,  // 1MB
 1024 * 100, // 100KB
 1024 * 10, // 10KB
 1024 * 1, // 1KB
 };
     char *data1;
     char *data2;
     int t1;
     int t2;
     int i = 0;
 ​
     data1 = (char *)malloc(size_list[0]);
     data2 = (char *)malloc(size_list[0]);
 ​
     get_rand_bytes(data1, size_list[0]);
 ​
     for (i = 0; i < ARRAY_SIZE(size_list); i++) {
     t1 = get_cur_time_us();
     memcpy(data2, data1, size_list[i]);
     t2 = get_cur_time_us();
     printf("copy %d bytes, memcpy_stdc   waste time %dus\\n", size_list[i], t2 - t1);
 ​
     t1 = get_cur_time_us();
     my_memcopy_normal(data2, data1, size_list[i]);
     t2 = get_cur_time_us();
     printf("copy %d bytes, memcpy_normal waste time %dus\\n", size_list[i], t2 - t1);
 ​
     t1 = get_cur_time_us();
     my_memcopy_super(data2, data1, size_list[i]);
     t2 = get_cur_time_us();
     printf("copy %d bytes, memcpy_super  waste time %dus\\n\\n", size_list[i], t2 - t1);
     }
 ​
     free(data1);
     free(data2);
 ​
 return 0;
 }
 ​

简单执行编译后,运行小程序的结果:

image-20221205135556210

从运行结果上看:

  • **拷贝数据量比较小时,拷贝效率排行: **normal < super < stdc
  • **拷贝数据量比较大时,拷贝效率排行: **normal < stdc < super

4 小小总结

memcpy的源码实现,核心就是内存拷贝,要想提升拷贝的效率,还得充分利用机器的运算性能,比如考虑对齐问题。

综合来看,标准C库实现的memcpy在大部分的场景下都可以有一个比较好的性能表现,这一点是值得称赞的。

5 更多分享

[架构师李肯]

架构师李肯全网同名 ),一个专注于嵌入式IoT领域的架构师。有着近10年的嵌入式一线开发经验,深耕IoT领域多年,熟知IoT领域的业务发展,深度掌握IoT领域的相关技术栈,包括但不限于主流RTOS内核的实现及其移植、硬件驱动移植开发、网络通讯协议开发、编译构建原理及其实现、底层汇编及编译原理、编译优化及代码重构、主流IoT云平台的对接、嵌入式IoT系统的架构设计等等。拥有多项IoT领域的发明专利,热衷于技术分享,有多年撰写技术博客的经验积累,连续多月获得RT-Thread官方技术社区原创技术博文优秀奖,荣获[CSDN博客专家]、[CSDN物联网领域优质创作者]、[2021年度CSDN&RT-Thread技术社区之星]、[2022年RT-Thread全球技术大会讲师]、[RT-Thread官方嵌入式开源社区认证专家]、[RT-Thread 2021年度论坛之星TOP4]、[华为云云享专家(嵌入式物联网架构设计师)]等荣誉。坚信【知识改变命运,技术改变世界】!

审核编辑:汤梓红

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • 函数
    +关注

    关注

    3

    文章

    4065

    浏览量

    61396
  • RT-Thread
    +关注

    关注

    31

    文章

    1165

    浏览量

    38973
  • memcpy
    +关注

    关注

    0

    文章

    9

    浏览量

    2793
收藏 人收藏

    评论

    相关推荐

    高效率的内存拷贝函数memcpy

    memcpy是memory copy的缩写,意为内存复制,在写C语言程序的时候,我们常常会用到它。
    发表于 11-08 09:48 6974次阅读

    Memset、Memcpy、Strcpy 的作用和区别(转)

    _testst[10]; memset(st,0,sizeof(struct _test)*10);//清空方法 //memset 源码的实现 C语言#includevoid* memset(void* s
    发表于 01-19 11:57

    HBase性能优化方法总结

    读密集型对于随机读密集型工作负载,高效利用缓存和更好地索引会给HBase系统带来更高性能2. 顺序读密集型对于顺序读密集型工作负载,可以采用不使用缓存的方式减少硬盘访问次数来提高性能
    发表于 04-20 17:16

    MSP430FRx MCU如何实现更高性能

    MSP MCU 的 MSP-IQMATHLIB 优化型软件库可通过提供优化型定点函数(包括加法、乘法、正弦和对数)来帮助您缩短产品上市时间。 相较于采用标准 math.h 头文件,这些函数
    发表于 09-10 11:57

    memCopy函数怎么实现拷贝的呢?

    memCopy函数是将指定地址的代码拷贝到目的地址,一般情况下是把flash的代码拷贝到ram内运行,问题是:在flash启动模式的情况下调用memcopy之前没有初始化flash的等
    发表于 05-12 08:39

    STM32中的memcpy函数的使用 精选资料推荐

    定义是什么memcpy 函数用于 把资源内存(src所指向的内存区域) 拷贝到目标内存(dest所指向的内存区域);拷贝多少个?有一个size变量控制
    发表于 08-24 08:11

    STM32字库拷贝函数

    STM32 字库拷贝函数,很好的学习资料,快来下载吧
    发表于 02-15 17:06 8次下载

    memcpy怎么用_memcpy用法总结

    memcpy指的是c和c++使用的内存拷贝函数memcpy函数的功能是从源src所指的内存地址的起始位置开始
    发表于 11-28 15:56 4.6w次阅读
    <b class='flag-5'>memcpy</b>怎么用_<b class='flag-5'>memcpy</b>用法总结

    C++:详谈拷贝构造函数

    只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这样的构造函数称为拷贝构造函数拷贝构造函数是特殊的构造
    的头像 发表于 06-29 11:45 1943次阅读
    C++:详谈<b class='flag-5'>拷贝</b>构造<b class='flag-5'>函数</b>

    C语言模拟实现memcpy函数

    memcpy指的是c和c++使用的内存拷贝函数memcpy函数的功能是从源src所指的内存地址的起始位置开始
    的头像 发表于 06-29 17:29 2306次阅读
    C语言模拟<b class='flag-5'>实现</b><b class='flag-5'>memcpy</b><b class='flag-5'>函数</b>

    C语言模拟实现memmove函数

    memmove用于从src拷贝count个字节到dest,如果目标区域和源区域有重叠的话,memmove能够保证源串在被覆盖之前将重叠区域的字节拷贝到目标区域中。但复制后src内容会被更改。但是当目标区域与源区域没有重叠则和
    的头像 发表于 06-29 17:53 1676次阅读
    C语言模拟<b class='flag-5'>实现</b>memmove<b class='flag-5'>函数</b>

    memcpy函数实现及其优化

    函数原型void * memcpy ( void * destination, const void * source, size_t num );
    发表于 12-09 14:25 2402次阅读

    C++之拷贝构造函数的浅copy及深copy

    C++编译器会默认提供构造函数;无参构造函数用于定义对象的默认初始化状态;拷贝构造函数在创建对象时拷贝对象的状态;对象的
    的头像 发表于 12-24 15:31 541次阅读

    C语言库memcpy和memmove的区别分析

    memcpy和memmove都是 C 语言的库函数,相比于 strcpy和 strncpy只能针对于字符类型的数组(),这两个函数可以拷贝其他类型的数组,对于
    发表于 09-19 12:19 1223次阅读

    memcpy和memmove的区别是什么

    `memcpy`和`memmove`都是 C语言的库函数,相比于 `strcpy`和 `strncpy`只能针对于字符类型的数组(),这两个函数可以拷贝其他类型的数组,对于 `
    的头像 发表于 01-20 16:55 2244次阅读
    <b class='flag-5'>memcpy</b>和memmove的区别是什么