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

news2024/12/23 11:44:25

【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 <string.h>

       void *memcpy(void *dest, const void *src, size_t n);

DESCRIPTION
       The memcpy() function copies n bytes from memory area src to memory area dest.  The memory areas must not overlap.  Use memmove(3) if the memory
       areas do overlap.

RETURN VALUE
       The memcpy() function returns a pointer to dest.

英文翻译过来就是说,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 <stdio.h>
#include <assert.h>

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、华为云云享专家(嵌入式物联网架构设计师)等荣誉。坚信【知识改变命运,技术改变世界】!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/71959.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

4D毫米波雷达开启感知新大陆,这家企业给出这样的答案

当前&#xff0c;自动驾驶行业的内卷已是不争的事实。无论是以叠加传感器为手段的“堆料”&#xff0c;还是以测试里程论性能高低的“堆数据”&#xff0c;最终都指向了同一个问题&#xff1a;感知瓶颈。随着自动驾驶行业越来越趋于理性&#xff0c;技术的研发也将回归最基础的…

【数据可视化】第五章—— 基于PyEcharts的数据可视化

文章目录1. pyecharts数据可视化介绍2&#xff0e;pyecharts安装与使用3&#xff0e;全局配置项和系列配置项3.1 全局配置项3.1.1 基本元素配置项3.1.2 坐标轴配置项3.1.3 原生图形配置项3.2 系列配置项3.2.1 样式类配置项3.2.2 标记类型配置项3.2.3 其它类配置项4&#xff0e;…

【性能优化】pc端与移动端图片优化篇

目录 优化方向&#xff1a; 优化方式 1.域名收敛 2.使用CDN节点 3.设置缓存 4.图片懒加载 5.用户图片上传限制 6.使用OSS服务压缩 7.使用OSS缩放 8.合成雪碧图 9.使用svg图片 在前端项目中图片的性能优化也有不少可以提升的方向 优化方向&#xff1a; 减少请求数量…

Go语言基础知识

Go语言基础知识 一、准备工作 1.1下载安装Go 地址&#xff1a;https://studygolang.com/dl 1、根据系统来选择下载包 2、下载完成后直接双击运行 3、一路next&#xff0c;注意选择安装路径 4、在控制台窗口输入“go version”可查看Go版本&#xff0c;检测是否安装成功 5、…

基于STAN的风力发电预测(Python代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f389;作者研究&#xff1a;&#x1f3c5;&#x1f3c5;&#x1f3c5;本科计算机专业&#xff0c;研究生电气学硕…

应届生自学Python两个月,为什么找不到工作?

今天我们来看一个同龄小伙伴自学Python的困惑&#xff0c;他自学了2个月Python就想找一份好工作&#xff0c;但是四处碰壁。对于这样的难题&#xff0c;我们来看看前辈们是如何解决的。 如果你想要学好python最好加入一个组织&#xff0c;这样大家学习的话就比较方便&#xff…

华夏教师杂志华夏教师杂志社华夏教师编辑部2022年第24期目录

视点_青少年党史学习教育《华夏教师》投稿&#xff1a;cn7kantougao163.com 青少年学党史的震阳实践 沙夕岗; 4-527 视点_德育 幸福365&#xff0c;成长每一天——苏州工业园区星澄学校“幸福365”德育课程简介 赵志德; 6-745 学校_基层党建 “1X”党员引领匠心路—…

高通Ride软件开发包使用指南(2)

高通Ride软件开发包使用指南&#xff08;2&#xff09;3 Ubuntu系统设置3.1前提条件3.2安装Ubuntu3.3 安装 docker3.4 在Linux主机上安装QNX软件中心3 Ubuntu系统设置 以下步骤准备Ubuntu系统使用docker图像构建工具链SDK&#xff0c;并启用可视化。 3.1前提条件 确保您的电…

三年PHP经验如何提高工资?PHP接单推荐

大家都知道&#xff0c;一般来说以PHP来做开发的有很大一部分是外包公司&#xff0c;如果简简单单的干了一到两年多&#xff0c;那么我相信你的经验值肯定会大幅度提升&#xff0c;但是如果说要真正提高技术或者很快的提高工资&#xff0c;这在我看来&#xff0c;相较于其他语言…

管理员必看!10+个Salesforce仪表板使用技巧

Salesforce仪表板是一种将报表中关键数据可视化的简单方法&#xff0c;可以帮助识别趋势、整理数量并衡量其活动的影响。 本篇文章将分享10个仪表板使用技巧&#xff0c;有效利用这个开箱即用的核心Salesforce功能&#xff0c;可以帮助管理员和用户事半功倍。 01 提高仪表板的…

二苯并环辛炔DBCO-PEG3-OTs,二苯并环辛炔-三聚乙二醇-对甲苯磺酰酯

●外观以及性质&#xff1a; DBCO&#xff08;二苯并环辛炔&#xff09;是一种环炔烃&#xff0c;可以通过在水溶液中通过应变促进的1,3-偶极环加成反应与叠氮化物反应&#xff0c;这种生物正交反应也称为无铜点击反应。DBCO 点击化学可以在水性缓冲液中运行&#xff0c;也可以…

高通Ride软件开发包使用指南(3)

高通Ride软件开发包使用指南&#xff08;3&#xff09;4准备软件下载平台4.1 CDT供应4.1.1使用QFIL的CDT编程4.1.2使用fastboot进行CDT编程4.1.3 CDT验证4准备软件下载平台 所有Snapdragon Ride参考平台都应已完成通用闪存&#xff08;UFS&#xff09;编程和配置数据表&#x…

spring 事务传播行为以及失效原因

今天在查看以前写的代码时&#xff0c;看到了事务的使用&#xff0c;感觉自己对这一块并不是特别清晰&#xff0c;所以就系统的学习了一下。在学习过程中发现很多地方自己以前理解的还是有点不对&#xff0c;所以记录一下学习笔记&#xff0c;希望帮助到大家。 一、事务传播行…

随机接入流程 - 2-Step RA

Overview 在LTE和R15 NR中&#xff0c;终端以及基站采用的接入技术均为四步随机接入(4-Step Random Access)技术&#xff0c;即终端和基站之间需要经过5次信息交互(这里我们所说是的基于竞争的随机接入过程&#xff0c;对于非竞争随机接入过程只需要3次信息交互)才能完成随机接…

m基于FPGA的数字下变频verilog设计

目录 1.算法描述 2.仿真效果预览 3.verilog核心程序 4.完整FPGA 1.算法描述 整个数字下变频的基本结构如下所示 NCO使用CORDIC算法&#xff0c;CIC采用h结构的CIC滤波器&#xff0c;HBF采用复用结构的半带滤波器&#xff0c;而FIR则采用DA算法结构。 这里&#xff0c;我们…

华硕编程竞赛11月JAVA专场 B题召唤精灵 题解

作者主页&#xff1a;Designer 小郑 作者简介&#xff1a;软件工程师一枚&#xff0c;来自浙江宁波&#xff0c;负责开发管理公司OA项目&#xff0c;专注软件前后端开发&#xff08;Vue、SpringBoot和微信小程序&#xff09;、系统定制、远程技术指导。CSDN学院、蓝桥云课认证讲…

ATtiny13与Proteus仿真-GPIO与点亮LED仿真

GPIO与点亮LED仿真 1、GPIO介绍 ATtiny13的端口具有(Read-Modify-Write,读取-更改-写入)基础功能。这就意味着,可以通过 SBI 和 CBI 指令更改一个端口引脚的方向,而不会影响其他端口方向。同时,如果更改一个端口的值,也不会影响其他的端口的值。 ATtiny13的端口输出缓冲…

Spring boot前后端分离项目使用docker-compose部署在Ubuntu上 以及内网穿透,外网访问项目

docker-compose.yml部署vue Springboot前后端分离项目 以及内网穿透&#xff0c;外网访问项目一、windows上部署前后端项目&#xff0c;测试1、前端Vue打包&#xff0c;放到nginx的html中2、nginx代理转发后端端口&#xff1a;4、后端Springboot项目使用Maven打包二、Linux上使…

-aop-

文章目录一.动态代理实现invocationHandler二.AOP概述环绕通知Pointcut定义切入点一.动态代理实现invocationHandler 动态代理&#xff1a;可以在程序的执行过程中&#xff0c;创建代理对象。 通过代理对象执行方法&#xff0c;给目标类的方法增加额外的功能&#xff08;功能增…

Filebeat采集数据到ES保证数据不重复

一.背景 业务需求使然&#xff0c;API接口负责收集用户传递上来的json数据&#xff0c;为了保证接口性能和数据的可靠性。我们没有直接拿到数据&#xff0c;然后存储到mysql或者kafka&#xff0c;而是直接使用最稳妥的方式&#xff0c;写文件。之后采用filebeat对数据文件进行采…