当前位置:  开发笔记 > 编程语言 > 正文

优化raw new []/delete [] vs std :: vector

如何解决《优化rawnew[]/delete[]vsstd::vector》经验,为你挑选了1个好方法。

让我们搞乱非常基本的动态分配内存.我们取3的向量,设置其元素并返回向量的总和.

在第一个测试用例中,我使用带new[]/ 的原始指针delete[].在我使用的第二个std::vector:

#include    

int main()
{
  //int *v = new int[3];        // (1)
  auto v = std::vector(3); // (2)


  for (int i = 0; i < 3; ++i)
    v[i] = i + 1;

  int s = 0;
  for (int i = 0; i < 3; ++i)
    s += v[i];

  //delete[] v;                 // (1)
  return s;
}

大会(1)(new[]/ delete[])

main:                                   # @main
        mov     eax, 6
        ret

大会(2)(std::vector)

main:                                   # @main
        push    rax
        mov     edi, 12
        call    operator new(unsigned long)
        mov     qword ptr [rax], 0
        movabs  rcx, 8589934593
        mov     qword ptr [rax], rcx
        mov     dword ptr [rax + 8], 3
        test    rax, rax
        je      .LBB0_2
        mov     rdi, rax
        call    operator delete(void*)
.LBB0_2:                                # %std::vector >::~vector() [clone .exit]
        mov     eax, 6
        pop     rdx
        ret

取自两个输出https://gcc.godbolt.org/与-std=c++14 -O3

在两个版本中,返回值都是在编译时计算的,所以我们只看到mov eax, 6; ret.

随着原始new[]/ delete[]动态分配被完全删除.随着std::vector然而,分配内存,设置和释放.

即使使用未使用的变量,也会 发生这种情况auto v = std::vector(3):调用new,设置内存然后调用delete.

我意识到这很可能是一个几乎不可能给出的答案,但也许有人有一些见解,可能会出现一些有趣的答案.

std::vector在原始内存分配情况下,哪些因素不允许编译器优化删除内存分配?



1> Hadi Brais..:

当使用指向动态分配的数组的指针(直接使用new []和delete [])时,编译器优化了对它们的调用operator new,operator delete即使它们具有可观察到的副作用.C++标准第5.3.4节第10段允许这种优化:

允许实现省略对可替换全局分配函数的调用(18.6.1.1,18.6.1.2).当它这样做时,存储由实现提供或者......

我会在结尾处显示句子的其余部分,这是至关重要的.

这种优化相对较新,因为它首先在C++ 14(提案N3664)中被允许.Clang从3.4开始支持它.最新版本的gcc,即5.3.0,没有利用as-if规则的这种放宽.它产生以下代码:

main:
        sub     rsp, 8
        mov     edi, 12
        call    operator new[](unsigned long)
        mov     DWORD PTR [rax], 1
        mov     DWORD PTR [rax+4], 2
        mov     rdi, rax
        mov     DWORD PTR [rax+8], 3
        call    operator delete[](void*)
        mov     eax, 6
        add     rsp, 8
        ret

MSVC 2013也不支持此优化.它产生以下代码:

main:
  sub         rsp,28h  
  mov         ecx,0Ch  
  call        operator new[] ()  
  mov         rcx,rax  
  mov         dword ptr [rax],1  
  mov         dword ptr [rax+4],2  
  mov         dword ptr [rax+8],3  
  call        operator delete[] ()  
  mov         eax,6  
  add         rsp,28h  
  ret 

我目前无法访问MSVC 2015 Update 1,因此我不知道它是否支持此优化.

最后,这是icc 13.0.1生成的汇编代码:

main:
        push      rbp                                          
        mov       rbp, rsp                                   
        and       rsp, -128                                    
        sub       rsp, 128                                     
        mov       edi, 3                                       
        call      __intel_new_proc_init                         
        stmxcsr   DWORD PTR [rsp]                               
        mov       edi, 12                                 
        or        DWORD PTR [rsp], 32832                       
        ldmxcsr   DWORD PTR [rsp]                               
        call      operator new[](unsigned long)
        mov       rdi, rax                                      
        mov       DWORD PTR [rax], 1                            
        mov       DWORD PTR [4+rax], 2                          
        mov       DWORD PTR [8+rax], 3                         
        call      operator delete[](void*)
        mov       eax, 6    
        mov       rsp, rbp                           
        pop       rbp                                   
        ret                                          

显然,它不支持这种优化.我无法访问最新版本的icc,即16.0.

所有这些代码片段都是在启用优化的情况下生成的.

使用时std::vector,所有这些编译器都没有优化分配.当编译器不执行优化时,它要么是因为它不能由于某种原因,要么它还不支持.

有哪些因素不允许编译器优化在std :: vector情况下删除内存分配,就像在原始内存分配情况下一样?

编译器没有执行优化,因为它不允许.为了看到这一点,让我们看看5.3.4中第10段的其余部分:

允许实现省略对可替换全局分配函数的调用(18.6.1.1,18.6.1.2).当它这样做时,存储由实现提供,或者通过扩展另一个新表达式的分配来提供.

这就是说,只有当它来自new-expression时,才能省略对可替换全局分配函数的调用.新表达式在同一节的第1段中定义.

以下表达式

new int[3]

是一个new-expression,因此允许编译器优化掉相关的分配函数调用.

另一方面,以下表达式:

::operator new(12)

不是一个新表达式(见5.3.4第1段).这只是一个函数调用表达式.换句话说,这被视为典型的函数调用.此函数无法优化,因为它从另一个共享库导入(即使您静态链接运行时,函数本身也调用另一个导入的函数).

使用的默认分配器使用std::vector分配内存::operator new,因此不允许编译器对其进行优化.

我们来试试吧.这是代码:

int main()
{
  int *v =  (int*)::operator new(12);

  for (int i = 0; i < 3; ++i)
    v[i] = i + 1;

  int s = 0;
  for (int i = 0; i < 3; ++i)
    s += v[i];

  delete v;
  return s;
}

通过使用Clang 3.7进行编译,我们得到以下汇编代码:

main:                                   # @main
        push    rax
        mov     edi, 12
        call    operator new(unsigned long)
        movabs  rcx, 8589934593
        mov     qword ptr [rax], rcx
        mov     dword ptr [rax + 8], 3
        test    rax, rax
        je      .LBB0_2
        mov     rdi, rax
        call    operator delete(void*)
.LBB0_2:
        mov     eax, 6
        pop     rdx
        ret

这与使用时生成的汇编代码完全相同,std::vector除了mov qword ptr [rax], 0来自std :: vector的构造函数之外(编译器应该删除它但由于其优化算法存在缺陷而无法执行此操作).

推荐阅读
大大炮
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有