为什么使用带有char的forward_list比使用long long更优化?

时间:2014-02-14 18:24:32

标签: c++ list optimization c++11 forward-list

我使用下面的代码将std::liststd::forward_list

进行比较
#include <iostream>
#include <list>
#include <forward_list>
#include <windows.h>

int main()
{
    LARGE_INTEGER start_;
    LARGE_INTEGER end_;
    LARGE_INTEGER freq;

    QueryPerformanceFrequency(&freq);
    std::list<long long> list;

    QueryPerformanceCounter(&start_);
    for(long long i=0;i<50000000;i++)
        list.push_front(i);
    QueryPerformanceCounter(&end_);


    std::cout<< (end_.QuadPart - start_.QuadPart) / (freq.QuadPart / 1000) <<"\n";
    cin.get();
}

我用forward_list更改了列表。 并测量两者的时间和大小 结果:

//long long
//              size    time
//forward list 1157.3  2360
//list         1157.3  2450

我也为此代码做了同样的事情:

int main()
{
    LARGE_INTEGER start_;
    LARGE_INTEGER end_;
    LARGE_INTEGER freq;

    QueryPerformanceFrequency(&freq);
    std::list<char> list;

    QueryPerformanceCounter(&start_);
    for(long long i=0;i<50000000;i++)
        list.push_front('a');
    QueryPerformanceCounter(&end_);


    std::cout<< (end_.QuadPart - start_.QuadPart) / (freq.QuadPart / 1000 )<<"\n";
    std::cin.get();
}

结果:

//char  
//              size  time
//forward list  773   2185
//list          1157  2400

问题是为什么使用std::forward_list whith char比使用long std::list长得多好。

我知道速度几乎相同,但容器的大小呢?

//long long
//             size(mb)    time
//forward list 1157.3      2360
//list         1157.3      2450


//char  
//              size(mb)  time
//forward list  773       2185
//list          1157      2400

2 个答案:

答案 0 :(得分:6)

首先要注意的是,这并不是“好得多”。差异完全是微不足道的。

除此之外,我认为复制8个字节需要比复制一个字节稍长一些;另外,您的常量'a'不需要读取寄存器/内存,其值可以硬编码到您的可执行文件中。

答案 1 :(得分:3)

forward_list通常实现为简单的单链表。列表节点具有指向列表中的以下节点的单个指针,以及保存实际用户数据的数据字段。 forward_list<T>的单个节点的大小将为sizeof(void*) + sizeof(T),假设(1)所有数据指针具有相同的大小,并且(2)T没有更严格的对齐比void*

list实现为双链表。因此,单个节点将具有数据字段和指向下一个和前一个列表节点的指针。因此list<T>节点的大小为2 * sizeof(void*) + sizeof(T),其假设与上述相同。

内存分配器通常在块中分配内存,这些块的大小是最大基本对齐的倍数。在提供分配的粒度和跟踪开销量之间存在权衡。以基本对齐的倍数分配块是一个很好的平衡点,并且还有额外的好处,即确保分配始终与任何未过度对齐的类型正确对齐。

让我们假设您的程序在32位实现上运行 - sizeof(void*) == 4 - 并且系统分配器使用8字节的块大小 - sizeof(double)。这些假设适用于MS Visual C ++实现的典型32位Windows C ++。根据这些假设,各种对象的大小为:

  • forward_list<char>节点:sizeof(void*) + sizeof(char) == 5,内存分配器将向上舍入为8字节分配。
  • forward_list<long long> node:sizeof(void*) + sizeof(long long) == 12,内存分配器将为16字节分配。
  • list<char> node:2 * sizeof(void*) + sizeof(char) == 9,内存分配器将为16字节分配。
  • list<long long>节点:2 * sizeof(void*) + sizeof(long long) == 16,内存分配器自16以来不会舍入为8的倍数。

对于OP中的类型,forward_list<char>每个元素分配8个字节,list<char>list<long long>forward_list<long long>的所有元素分配16个字节。这是对内存使用情况观察的一种可能解释。