百万点数组下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 进行特殊的优化,因为它们是标准库函数,编译器可以更好地预测它们的行为,并进行针对性的优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AI+程序员在路上

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值