首页 > 其他分享 >大量创建对象的性能

大量创建对象的性能

时间:2023-07-10 12:23:43浏览次数:32  
标签:begin usec int 性能 创建对象 test 大量 end now

大量创建对象的性能

注:试验中,均统一开启 O3 优化。试验均在同一系统上进行。

背景

在构建一种通用图片对象时,一种可能的实现是将每个像素作为一个对象处理。

以2560*1440为例,约\(3.6\times 10^6\)像素,因此在构建图片时,需要创建大量像素对象。

本文讨论C++下创建大量对象的性能以及是否有优化空间。

试验-malloc

使用memset初始化

int main()
{
    int obj_num = 1 * 1000 * 1000;
    
    int64_t begin = usec_now();
    // empty op
    int64_t end = usec_now();

    printf("empty: %d\n", (int)(end - begin));
    
    begin = usec_now();
    int *a = (int*)malloc(obj_num * sizeof(int));
    end = usec_now();

    printf("malloc: %d\n", (int)(end - begin));

    begin = usec_now();
    memset (a, 0x7, sizeof(obj_num * sizeof(int)));
    end = usec_now();

    printf("memset: %d\n", (int)(end - begin));
    
    begin = usec_now();
    free(a);
    end = usec_now();

    printf("free: %d\n", (int)(end - begin));
    
    return 0;
}

运行结果:

test@ubuntu:~/tmp$ g++ test.cpp -std=c++11 -O3
test@ubuntu:~/tmp$ ./a.out
empty: 0
malloc: 0
memset: 0
free: 0

使用for初始化

int main()
{
    int obj_num = 1 * 1000 * 1000;
    
    int64_t begin = usec_now();
    // empty op
    int64_t end = usec_now();

    printf("empty: %d\n", (int)(end - begin));
    
    begin = usec_now();
    int *a = (int*)malloc(obj_num * sizeof(int));
    end = usec_now();

    printf("malloc: %d\n", (int)(end - begin));

    begin = usec_now();
    for (int i = 0; i < obj_num; ++i)
    {
        a[i] = 0x7;
    }
    end = usec_now();

    printf("memset: %d\n", (int)(end - begin));
    
    begin = usec_now();
    free(a);
    end = usec_now();

    printf("free: %d\n", (int)(end - begin));
    
    return 0;
}

运行结果如下:

test@ubuntu:~/tmp$ g++ test.cpp -std=c++11 -O3
test@ubuntu:~/tmp$ ./a.out
empty: 0
malloc: 6
memset: 1606
free: 426

结论

部分平台的memset有汇编优化,可以用来快速初始化大量内存;

试验-new

使用new[]创建大量默认构造和析构函数对象

试验代码如下:

// g++ test.cpp -std=c++11 -O3

#include <stdio.h>
#include <vector>
#include <stdint.h>
#include <time.h>
#include <chrono>

int64_t usec_now()
{
    auto now = std::chrono::steady_clock::now();
    auto usec = std::chrono::time_point_cast<std::chrono::microseconds>(now).time_since_epoch().count();
    return usec;
}

// 创建大量对象并计时
class Obj
{
public: 
    // Obj() {};
    // ~Obj() {};
private:
    int i;
};

int main()
{
    int obj_num = 1 * 1000 * 1000;
    
    int64_t begin = usec_now();
    // empty op
    int64_t end = usec_now();

    printf("empty: %d\n", (int)(end - begin));
    
    begin = usec_now();
	Obj *objs = new Obj[obj_num];
    end = usec_now();
    printf("construct: %d\n", (int)(end - begin));
    
    begin = usec_now();
	delete [] objs;
    end = usec_now();
    printf("destruct: %d\n", (int)(end - begin));
    
    return 0;
}

运行结果如下:

test@ubuntu:~/tmp$ g++ test.cpp -std=c++11 -O3
test@ubuntu:~/tmp$ ./a.out
empty: 1
construct: 8
destruct: 8

使用new[]创建有空构造函数和默认析构函数对象

class Obj
{
public: 
    Obj() {};
    // ~Obj() {};
private:
    int i;
};

运行结果:

test@ubuntu:~/tmp$ g++ test.cpp -std=c++11 -O3
test@ubuntu:~/tmp$ ./a.out
empty: 0
construct: 7
destruct: 7

使用new[]创建默认构造函数和有空析构函数对象

class Obj
{
public: 
    // Obj() {};
    ~Obj() {};
private:
    int i;
};

运行结果:

test@ubuntu:~/tmp$ g++ test.cpp -std=c++11 -O3
test@ubuntu:~/tmp$ ./a.out
empty: 0
construct: 7
destruct: 602

使用new[]创建有空构造函数和有空析构函数对象

class Obj
{
public: 
    Obj() {};
    ~Obj() {};
private:
    int i;
};

运行结果:

test@ubuntu:~/tmp$ g++ test.cpp -std=c++11 -O3
test@ubuntu:~/tmp$ ./a.out
empty: 0
construct: 6
destruct: 567

使用new[]创建,构造时指定默认值

class Obj
{
public: 
    Obj() {};
    ~Obj() {};
private:
    int i{ 0 };
};

运行结果:

test@ubuntu:~/tmp$ g++ test.cpp -std=c++11 -O3
test@ubuntu:~/tmp$ ./a.out
empty: 0
construct: 1934
destruct: 913

使用new直接创建int数组

int main()
{
    int obj_num = 1 * 1000 * 1000;
    
    int64_t begin = usec_now();
    // empty op
    int64_t end = usec_now();

    printf("empty: %d\n", (int)(end - begin));
    
    begin = usec_now();
	// Obj *objs = new Obj[obj_num];
    int *arr = new int[obj_num];
    end = usec_now();
    printf("construct: %d\n", (int)(end - begin));
    
    begin = usec_now();
	// delete [] objs;
    delete [] arr;
    end = usec_now();
    printf("destruct: %d\n", (int)(end - begin));
    
    return 0;
}

运行结果:

test@ubuntu:~/tmp$ g++ test.cpp -std=c++11 -O3
test@ubuntu:~/tmp$ ./a.out
empty: 0
construct: 7
destruct: 7

使用new直接创建int数组并初始化

int main()
{
    int obj_num = 1 * 1000 * 1000;
    
    int64_t begin = usec_now();
    // empty op
    int64_t end = usec_now();

    printf("empty: %d\n", (int)(end - begin));
    
    begin = usec_now();
    int *a = new int[obj_num]{0x7};
    end = usec_now();

    printf("construct: %d\n", (int)(end - begin));
    
    begin = usec_now();
    delete [] a;
    end = usec_now();

    printf("destruct: %d\n", (int)(end - begin));
    
    return 0;
}

运行结果:

test@ubuntu:~/tmp$ g++ test.cpp -std=c++11 -O3
test@ubuntu:~/tmp$ ./a.out
empty: 0
construct: 1806
destruct: 240

结论

指定有内容的构造函数之后,运行时间相比直接申请内存约有100倍的增加。

对象的构造及初始化和直接使用new创建并初始化的时间复杂度是同级别的。

对象的构造及初始化和使用for进行初始化的时间复杂度是同级别的。

试验-vector

生成int数组

int main()
{
    int obj_num = 1 * 1000 * 1000;
    
    int64_t begin = usec_now();
    // empty op
    int64_t end = usec_now();

    printf("empty: %d\n", (int)(end - begin));
    
    begin = usec_now();
    std::shared_ptr<std::vector<int>> vec = std::make_shared<std::vector<int>>(obj_num);
    end = usec_now();

    printf("construct: %d\n", (int)(end - begin));
    
    begin = usec_now();
    vec.reset();
    end = usec_now();

    printf("destruct: %d\n", (int)(end - begin));
    
    return 0;
}

运行结果:

test@ubuntu:~/tmp$ ./a.out
empty: 0
construct: 1698
destruct: 415

生成对象数组

int main()
{
    int obj_num = 1 * 1000 * 1000;
    
    int64_t begin = usec_now();
    // empty op
    int64_t end = usec_now();

    printf("empty: %d\n", (int)(end - begin));
    
    begin = usec_now();
    std::shared_ptr<std::vector<Obj>> vec = std::make_shared<std::vector<Obj>>(obj_num);
    end = usec_now();

    printf("construct: %d\n", (int)(end - begin));
    
    begin = usec_now();
    vec.reset();
    end = usec_now();

    printf("destruct: %d\n", (int)(end - begin));
    
    return 0;
}

运行结果:

test@ubuntu:~/tmp$ g++ test.cpp -std=c++11 -O3
test@ubuntu:~/tmp$ ./a.out
empty: 0
construct: 1812
destruct: 240

结论

使用vector生成int数组和对象数组,时间复杂度与for初始化级别相同。

优化方案和结论

创建大量对象时,其时间复杂度和直接申请这些对象的内存并初始化的时间复杂度接近,并且使用容器去创建也是一样的时间复杂度。

如果需要提高大量对象创建的性能,一种可能的方法是使用已有的memset来进行初始化。如下:

template <typename _Size>
struct Pixel
{
    uint8_t mem[_Size];
    int size();
    int color(int index);
};

int main()
{
    using Pixel4 = Pixle<4>;
    std::size_t size = sizeof(Pixel4) * width * height;
	Pixel4 *pixel = malloc(size);
    memset(pixel, 0, size);
    
    return 0;
}

值得注意的是,此时被初始化的对象的成员必须是能够被memset的。

标签:begin,usec,int,性能,创建对象,test,大量,end,now
From: https://www.cnblogs.com/amazzzzzing/p/17540740.html

相关文章

  • 中小型系统必要可行的性能测试实践--ArtHas调优实战
    一、参考arthas用户实例,积累arthas调优经验。二、arthas命令汇总图 参考:arthas官网文档arthas在线教程arthas用户案例一图掌握Artha ......
  • mysql开启远程连接的权限及性能优化详解
    最近使用NavicatforMySQl访问远程mysql数据库,出现报错,显示“1130-Host’xxx.xxx.xxx.xxx’isnotallowedtoconnecttothisMySQLserver“。经查阅,错误原因是:本地IP(xxx.xxx.xxx.xxx)没有访问远程数据库的权限。于是下面开启本地IP(xxx.xxx.xxx.xxx)对远程mysql数据库的访......
  • GMSK调制解调器 matlab viterbi解调采用维特比解调性能具有很大优势
    GMSK调制解调器matlabviterbi解调采用维特比解调性能具有很大优势GMSK调制解调器是一种使用GaussianMinimumShiftKeying(GMSK)调制技术的设备,而Matlab是一种常用的数学软件。在GMSK调制解调器中,Viterbi解调是一种常用的解调方法,它通过使用Viterbi算法来提高解调性能。GMSK调制......
  • MySQL之慢 SQL 定位、性能优化实践
    MySQL之慢SQL定位、性能优化实践IT大咖说 2023-07-0520:00 发表于浙江在面试中被问到MySQL慢查询的概率还是非常高的。说你没有经历过就是理由吗?显然不是。一般来说一句SQL语句执行超过5s就能够算是慢SQL,需要进行优化了。◆ 为何要对慢SQL进行治理每一个SQL......
  • Qt/C++原创项目作品精选(祖传原创/性能凶残)
    00前言说明从事Qt开发十年有余,一开始是做C#.NET开发的,因为项目需要,转行做嵌入式linux开发,在嵌入式linux上做可视化界面开发一般首选Qt,当然现在可选的方案很多比如安卓,但是十多年前那时候板子性能低,安卓在这个上面跑的话卡成屎,当时的内存大概是128MB左右,Qt也是主要用QWidget,刚出......
  • 一次简单的Java服务性能优化,实现压测 QPS 翻倍
    背景前段时间我们的服务遇到了性能瓶颈,由于前期需求太急没有注意这方面的优化,到了要还技术债的时候就非常痛苦了。在很低的QPS压力下服务器load就能达到10-20,CPU使用率60%以上,而且在每次流量峰值时接口都会大量报错,虽然使用了服务熔断框架Hystrix,但熔断后服务却迟迟不......
  • 锐龙5 7600搭配A620主板大战i5-13490F:整体便宜800元 游戏性能完全持平
    一、前言:搭配A620主板后锐龙57600能否战胜i5-13490F?作为当前售价最低的Zen4桌面处理器,锐龙57600在游戏性能上要远超同价位的i5-13400,而随着近期A620主板的大量上市,AMD平台的性价比又到了进一步提升。那么问题来了:如果搭配价格便宜的A620主板,锐龙57600到底会损失多少性能呢?......
  • Linux 系统性能优化
    一、影响Linux性能的各种因素1、系统硬件资源(1)CPU如何判断多核CPU与超线程消耗CPU的业务:动态web服务、mail服务(2)内存物理内存与 swap的取舍选择64位Linux操作系统消耗内存的业务:内存数据库(redis/hbase/mongodb)(3)磁盘IORAID技术(RAID0/1/5/01/10......
  • EasyCVR接入大量设备级联后出现分组加载异常是什么原因?
    EasyCVR可拓展性强、视频能力灵活、部署轻快,可支持的主流标准协议有GB28181、RTSP/Onvif、RTMP等,以及厂家私有协议与SDK接入,包括海康Ehome、海大宇等设备的SDK等,能对外分发RTSP、RTMP、FLV、HLS、WebRTC等格式的视频流。有用户反馈,在EasyCVR用户现场接入了大量设备,出现了设备分......
  • 性能测试工具loadrunner学习
    一、性能测试基础1.性能测试概述  性能测试分类 性能测试方式时通过模拟生产运行的业务压力量和使用场景组合,测试系统的性能是否满足生产性能要求,通俗地讲,这种方法就是要在特定的运行条件下验证系统的能力状况。 ......