当前位置: 首页 > news >正文

百万点数组下memset、memcpy与for循环效率对比及原理分析

一.概述

    做上百万数组赋值及拷贝的时候,不得不考虑效率问题,一般计算机低于十万的数组赋值拷贝基本不用考虑效率问题(基本在1毫秒内完成),本文会对百万以上的数组下赋值及拷贝进行效率分析和对比。

二.代码实测

1.测试环境:QT5

2.测试代码

#include <QTime>

#include <QDebug>

#include <stdio.h>

#include <string.h>

#define ARRAY_SIZE    5000000  //五百万

//必须定义为全局数组,不能定义为局部变量,数组太大,定义为局部变量运行会崩溃

double array1[ARRAY_SIZE];

// 定义第二个包含100万个double元素的数组

double array2[ARRAY_SIZE];

// 用于赋值的源数组

double source_array[ARRAY_SIZE];

int main(int argc, char *argv[])

{

    // 定义第一个包含100万个double元素的数组

    for (int i = 0; i < ARRAY_SIZE; i++) {

        source_array[i] = (double)i;

    }

    qDebug() << "当前时间1:" << QTime::currentTime().toString("hh:mm:ss.zzz");

    // 使用for循环将array1清零

    for (int i = 0; i < ARRAY_SIZE; i++) {

        array1[i] = 0.0;

    }

    qDebug() << "当前时间11:" << QTime::currentTime().toString("hh:mm:ss.zzz");

    // 使用memset将array1再次清零

    qDebug() << "当前时间2:" << QTime::currentTime().toString("hh:mm:ss.zzz");

    memset(array1, 0, sizeof(double) * ARRAY_SIZE);

    qDebug() << "当前时间22:" << QTime::currentTime().toString("hh:mm:ss.zzz");

    // 使用for循环对array2进行赋值

    qDebug() << "当前时间3:" << QTime::currentTime().toString("hh:mm:ss.zzz");

    for (int i = 0; i < ARRAY_SIZE; i++) {

        array2[i] = source_array[i];

    }

    qDebug() << "当前时间33:" << QTime::currentTime().toString("hh:mm:ss.zzz");

    // 使用memcpy对array2进行内存拷贝赋值

    qDebug() << "当前时间4:" << QTime::currentTime().toString("hh:mm:ss.zzz");

    memcpy(array2, source_array, sizeof(double) * ARRAY_SIZE);

    qDebug() << "当前时间44:" << QTime::currentTime().toString("hh:mm:ss.zzz");

    return 0;

}

3.测试结果

  五百万点的情况下log打印:

当前时间1: "10:27:43.122"

当前时间11: "10:27:43.136"

当前时间2: "10:27:43.136"

当前时间22: "10:27:43.143"

当前时间3: "10:27:43.143"

当前时间33: "10:27:43.158"

当前时间4: "10:27:43.158"

当前时间44: "10:27:43.163"

可以看到memset和memcpy是for循环时间的40%~50%,也就是效率能提升一倍。

三.原理分析

1.函数实现的底层优化

memset 和 memcpy 的底层实现:memset 和 memcpy 是标准库函数,它们在不同的操作系统和编译器中都经过了高度的底层优化。这些函数通常是用汇编语言实现的,汇编语言可以直接操作计算机的底层硬件,绕过了高级语言的一些开销。

示例:在 x86 架构上,memcpy 可能会使用 SSE(Streaming SIMD Extensions)或 AVX(Advanced Vector Extensions)等指令集。这些指令集允许在一个时钟周期内同时处理多个数据元素,从而显著提高了数据复制的速度。

2.减少函数调用开销

for 循环的函数调用开销:当使用 for 循环来复制或设置内存时,每次循环都可能涉及到一些额外的操作,如循环条件的判断、循环变量的更新等。这些操作在每次循环时都会重复执行,增加了程序的开销。

memset 和 memcpy 的一次性调用:memset 和 memcpy 是一次性调用的函数,它们在函数内部完成所有的内存操作,避免了多次函数调用的开销。

3.数据访问模式的优化

for 循环的数据访问模式:在 for 循环中,每次循环通常只处理一个数据元素,这种逐元素的访问模式可能会导致频繁的内存访问,从而降低了效率。

memset 和 memcpy 的批量数据处理:memset 和 memcpy 会以更大的块为单位来处理数据,这样可以减少内存访问的次数,提高缓存命中率。例如,memcpy 可能会一次复制 4 字节、8 字节甚至 16 字节的数据,而不是每次只复制一个字节。

4.编译器优化

for 循环的编译器优化限制:虽然现代编译器会对 for 循环进行一些优化,但由于 for 循环的逻辑可能比较复杂,编译器的优化能力有限。

memset 和 memcpy 的编译器优化:编译器通常会对 memset 和 memcpy 进行特殊的优化,因为它们是标准库函数,编译器可以更好地预测它们的行为,并进行针对性的优化。

相关文章:

  • 【大模型与AIGC深度解析】从核心概念到行业应用
  • Python实现孔填充与坐标转换
  • 网络编程——通信三要素
  • GitLab_密钥生成(SSH-key)
  • 第4天:Linux开发环境搭建
  • 【JavaScript】详讲运算符--算术运算符
  • 时间自动填写——电子表格公式的遗憾(DeepSeek)
  • 品融电商:领航食品类目全域代运营,打造品牌增长新引擎
  • EasySearch 服务昨天还好好的,为什么今天突然访问不了了?
  • Java面试题汇总
  • FI固定资产折旧码的功能用途及其配置介绍
  • 【CSS】层叠,优先级与继承(三):超详细继承知识点
  • 推荐系统排序阶段核心要点:多目标排序模型详解
  • UnoCSS原子CSS引擎-前端福音
  • 15.FineReport动态展示需要的列
  • 数据库-基本概述 和 SQL 语言
  • 记录一次OGG进程abended,报错OGG-01431、OGG-01003、OGG-01151、OGG-01296问题的处理
  • C++入门基础(2)
  • Generative AI for Krita - Krita 生成式 AI 插件
  • 【QT】信号与槽中多个按钮(pushbutton)共用一个槽函数的两种实现方式
  • 范福生受审:任高密市长、市委书记时滥用职权,致公共财产利益重大损失
  • 观察|动力电池步入“多核时代”,宁德时代新技术密集开箱有何启示
  • 4月语言学联合书单|法庭审判话语的态度表达及人际功能研究
  • 大理杨徐邱上诉案开庭:当事人称曾接受过两次测谎测试
  • 云南巧家警方抓获一名网逃人员:带70余万现金“隐居”山洞,昼伏夜出
  • 郑州卫健委通报郑飞医院“血液净化”问题:拟撤销该院血液净化技术备案