关于c ++:在汇编代码中寻找不必要的缓冲区副本

Looking for unnecessary buffer copies in assembly code

我正在将Visual Studio 2008 C ++用于Windows Mobile 6 ARMV4I,并且正在尝试学习阅读VS生成的ARM汇编代码,以最大程度地减少应用程序内不必要的缓冲区副本。 因此,我创建了一个如下所示的测试应用程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <vector>

typedef std::vector< BYTE > Buf;

class Foo
{
public:
    Foo( Buf b ) { b_.swap( b ); };
private:
    Buf b_;
};

Buf Create()
{
    Buf b( 1024 );
    b[ 0 ] = 0x0001;
    return b;
}

int _tmain( int argc, _TCHAR* argv[] )
{
    Foo f( Create() );
    return 0;
}

我想了解在将Create返回的缓冲区提供给Foo构造函数时是否已复制,或者编译器是否能够优化该副本。 在启用了优化的Release版本中,将生成如下所示的程序集:

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
80
81
82
class Foo
{
public:
    Foo( Buf b ) { b_.swap( b ); };
0001112C  stmdb       sp!, {r4 - r7, lr}
00011130  mov         r7, r0
00011134  mov         r3, #0
00011138  str         r3, this
0001113C  str         r3, [r7, #4]
00011140  str         r3, [r7, #8]
00011144  ldr         r3, this
00011148  ldr         r2, this
0001114C  mov         r5, r7
00011150  mov         r4, r1
00011154  str         r3, this, #4
00011158  str         r2, this, #4
0001115C  mov         r6, r1
00011160  ldr         r2, this
00011164  ldr         r3, this
00011168  mov         lr, r7
0001116C  str         r3, this
00011170  str         r2, this
00011174  ldr         r2, [lr, #8]!
00011178  ldr         r3, [r6, #8]!
0001117C  str         r3, this
00011180  str         r2, this
00011184  ldr         r3, this
00011188  movs        r0, r3
0001118C  beq         |Foo::Foo + 0x84 ( 111b0h )|
00011190  ldr         r3, [r1, #8]
00011194  sub         r1, r3, r0
00011198  cmp         r1, #0x80
0001119C  bls         |Foo::Foo + 0x80 ( 111ach )|
000111A0  bl          000112D4
000111A4  mov         r0, r7
000111A8  ldmia       sp!, {r4 - r7, pc}
000111AC  bl          |stlp_std::__node_alloc::_M_deallocate ( 11d2ch )|
000111B0  mov         r0, r7
000111B4  ldmia       sp!, {r4 - r7, pc}
--- ...\stlport\stl\_vector.h -----------------------------
// snip!
--- ...\asm_test.cpp
    private:
        Buf b_;
    };

Buf Create()
{
00011240  stmdb       sp!, {r4, lr}
00011244  mov         r4, r0
    Buf b( 1024 );
00011248  mov         r1, #1, 22
0001124C  bl          |    
    b[ 0 ] = 0x0001;
00011250  ldr         r3, [r4]
00011254  mov         r2, #1
    return b;
}

int _tmain( int argc, _TCHAR* argv[] )
{
00011264  str         lr, [sp, #-4]!
00011268  sub         sp, sp, #0x18
    Foo f( Create() );
0001126C  add         r0, sp, #0xC
00011270  bl          |Create ( 11240h )|
00011274  mov         r1, r0
00011278  add         r0, sp, #0
0001127C  bl          |Foo::Foo ( 1112ch )|
    return 0;
00011280  ldr         r0, argc
00011284  cmp         r0, #0
00011288  beq         |wmain + 0x44 ( 112a8h )|
0001128C  ldr         r3, [sp, #8]
00011290  sub         r1, r3, r0
00011294  cmp         r1, #0x80
00011298  bls         |wmain + 0x40 ( 112a4h )|
0001129C  bl          000112D4
000112A0  b           |wmain + 0x44 ( 112a8h )|
000112A4  bl          |stlp_std::__node_alloc::_M_deallocate ( 11d2ch )|
000112A8  mov         r0, #0
}

我可以在汇编代码中寻找什么模式来了解Buf结构的复制位置?


分析Create非常简单,因为代码太短了。 NRVO显然已在此处应用,因为return语句未生成指令,返回值在r0中就地构造。

Foo::Foo的传递值参数将要进行的复制要稍微难点分析,但是在CreateFoo::Foo的调用之间必须进行复制的代码很少,并且 没有什么可以做std::vector的深层复制。 因此,看起来该副本也已被删除。 另一种可能性是Foo::Foo的自定义调用约定,其中参数实际上是通过引用传递并复制到函数内部的。 您需要一个能够进行更深入的ARM组装分析的人员来排除这一点。


缓冲区将被复制; 您正在使用c ++的按值传递语义; 没有编译器会为您优化它。 其复制方式将取决于std :: vector的复制构造函数。