大量创建对象的性能
注:试验中,均统一开启 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