发布于 

不同内存分配方式的时间差异

不同内存分配方式的时间差异

自从开始熟悉的cpp的指针写法后,我在使用疯狂使用指针的路上一去不复返(苦笑)。然而,自此之后,我发现自己的程序总是比别人慢半拍(其实不止半拍。。)。于是想要比较一下不同写法的差异。没想得,只是写了一下内存分配的部分,就发现了问题的根源所在。。。

分析

在这里,我们通过对新建\(10^7\)Node结构体来比较不同的分配方法所花费的时间。并且,我们在分配完成后进行一次初始化操作。Node结构体如下:

1
2
3
4
5
6
struct Node
{
int x;

Node (int x = 0):x(x) {}
};

我们的所有操作都是在以下的循环体内进行:

1
2
3
4
for (int i = 0; i<MAXN; i++)
{
true//code here
}

首先,我们考虑的是最常用的情况:直接定义,赋值。代码为:Node x = Node(i);。经多次实验,所花费的时间大概为31ms

然后,便是指针写法。代码为:Node *x = new Node(i);。所花费时间大致为500ms600ms

此外,我们尝试了一下提前获取内存空间,然后在需要指针时将已经获取到的内存空间分配给当前指针的写法。这里的写法是一个较为普适性的写法,使用了一个MemoryPool的结构体,若是只需分配一种结构体的空间则可以不用这么麻烦。MemoryPool的写法参照于Menci,写法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
template <size_t SIZE>
struct MemoryPool
{
char buf[SIZE], *cur;

MemoryPool():cur(buf) {}

void *malloc(int size)
{
if (size + cur > buf + SIZE)
{
cout << "out of memory" << endl;
return malloc(size);
}

char *p = cur;
cur += size;
return (void *)p;
}
};

MemoryPool<sizeof(Node) * MAXN> pool;
然后再循环内执行下列代码即可:
1
2
Node *x = (Node*)pool.malloc(sizeof(Node));
*x = Node(i);

该方法的循环执行时间大概为90ms,加上大致30ms的初始内存分配时间,大致为120ms

另外,此时的循环内代码通过引用new头文件(#include <new>),可以简写为Node *x = new ((Node*)pool.malloc(sizeof(Node))) Node(i);,对运行时间没有什么影响。

由此,我们可以得出结论。各方法的时间比约为\(1:20:4\)。也就是说,若是为了运行效率考虑,还是普通的直接定义使用的方法优先。但若是想用指针又想较为快速的话,也可以使用第三种方法,不过这样令变量的生命周期更加难于管理,所以在实际工程中不建议使用,在竞赛中可以作为优化指针速度的一种手段。

完整代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <iostream>
#include <new>
#include <windows.h>

#define MAXN 10000000

using namespace std;

struct Node
{
int x;

Node (int x = 0):x(x) {}
};

template <size_t SIZE>
struct MemoryPool
{
char buf[SIZE], *cur;

MemoryPool():cur(buf) {}

void *malloc(int size)
{
if (size + cur > buf + SIZE)
{
cout << "out of memory" << endl;
return malloc(size);
}

char *p = cur;
cur += size;
return (void *)p;
}
};

MemoryPool<2 * sizeof(Node) * MAXN> pool;

int main()
{
DWORD a = GetTickCount();

//方法一:直接定义
for (int i = 0; i<MAXN; i++)
{
Node x = Node(i);
}

DWORD b = GetTickCount();

//方法二:使用指针
for (int i = 0; i<MAXN; i++)
{
Node *x = new Node(i);
}

DWORD c = GetTickCount();

//方法三:提前分配空间,然后分配空间
for (int i = 0; i<MAXN; i++)
{
Node *x = (Node*)pool.malloc(sizeof(Node));
*x = Node(i);
}

DWORD d = GetTickCount();

//方法四:在方法三的基础上使用new方法
for (int i = 0; i<MAXN; i++)
{
Node *x = new ((Node*)pool.malloc(sizeof(Node))) Node(i);
}

DWORD e = GetTickCount();

cout << "The time we spent were " << b - a << ": " << c - b << ": " << d - c << ": " << e - d << endl;

return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

本站由 [@Songer](https://blog.songer.xyz/) 创建,使用 Stellar 作为主题。