【问题标题】:Why using forward_list with char is much more optimize than using it with long long?为什么将 forward_list 与 char 一起使用比与 long long 一起使用更优化?
【发布时间】:2014-02-14 18:24:32
【问题描述】:

我使用下面的代码来比较 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 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

【问题讨论】:

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


【解决方案1】:

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

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

【讨论】:

  • @omid:不知道你为什么认为这很重要。
【解决方案2】:

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

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

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

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

  • forward_list&lt;char&gt; 节点:sizeof(void*) + sizeof(char) == 5,内存分配器会将其四舍五入为 8 字节分配。
  • forward_list&lt;long long&gt; 节点:sizeof(void*) + sizeof(long long) == 12,内存分配器会将其四舍五入为 16 字节分配。
  • list&lt;char&gt; 节点:2 * sizeof(void*) + sizeof(char) == 9,内存分配器会将其四舍五入为 16 字节分配。
  • list&lt;long long&gt; 节点:2 * sizeof(void*) + sizeof(long long) == 16,内存分配器不会舍入它,因为 16 已经是 8 的倍数。

所以在 OP 中的类型中,forward_list&lt;char&gt; 为每个元素分配 8 个字节,list&lt;char&gt;list&lt;long long&gt;forward_list&lt;long long&gt; 的所有类型为每个元素分配 16 个字节。这是内存使用观察的一种可能解释。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-31
    • 1970-01-01
    • 1970-01-01
    • 2015-08-14
    • 1970-01-01
    • 2020-09-01
    相关资源
    最近更新 更多