- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
看来具有优化功能的G++无法内联来自转换单元静态变量的琐碎函数。下面的代码和编译输出示例。注意,函数 can_inline_local 通过使用DerivedType的本地实例完美地内联了调用,但是 cant_inline_static 的调用时间更长。
在您对我进行过早的优化报警之前,我想为自己辩护,说多态继承将非常清楚地描述我的内核级串行驱动程序中断服务例程。而且如果G++只能为我内联虚拟调用(使用我在编译时应该知道的信息),那么我将拥有可编译为C性能的清晰可测试的代码。
我正在使用arm-none-eabi-g++ -v
gcc版本4.9.3 20150529(预发行)(15:4.9.3 + svn227297-1)
arm-none-eabi-g++ -std = gnu ++ 11 -O3 -c -o inline.o inline.cpp && arm-none-eabi-objdump inline.o -S> inline.dump
inline.cpp:
extern "C"{
int * const MEMORY_MAPPED_IO_A = (int*)0x40001000;
int * const MEMORY_MAPPED_IO_B = (int*)0x40002000;
}
namespace{
/** Anon namespace should make these
typedefs static to this translation unit */
struct BaseType{
void* data;
virtual void VirtualMethod(int parameter){
*MEMORY_MAPPED_IO_A = parameter;
}
void VirtualCaller(int parameter){
this->VirtualMethod(parameter);
}
};
struct DerivedType : BaseType{
void VirtualMethod(int parameter) final {
*MEMORY_MAPPED_IO_B = parameter;
}
};
/** static keyword here may be superfluous */
static BaseType basetype;
static DerivedType derivedtype;
extern "C"{
void cant_inline_static(int parameter){
derivedtype.VirtualCaller(1);
}
void can_inline_local(int parameter){
DerivedType localobj;
localobj.VirtualCaller(1);
}
}
}
inline.o: file format elf32-littlearm
Disassembly of section .text:
00000000 <_ZN12_GLOBAL__N_18BaseType13VirtualMethodEi>:
0: e59f3004 ldr r3, [pc, #4] ; c <_ZN12_GLOBAL__N_18BaseType13VirtualMethodEi+0xc>
4: e5831000 str r1, [r3]
8: e12fff1e bx lr
c: 40001000 .word 0x40001000
00000010 <_ZN12_GLOBAL__N_111DerivedType13VirtualMethodEi>:
10: e59f3004 ldr r3, [pc, #4] ; 1c <_ZN12_GLOBAL__N_111DerivedType13VirtualMethodEi+0xc>
14: e5831000 str r1, [r3]
18: e12fff1e bx lr
1c: 40002000 .word 0x40002000
00000020 <cant_inline_static>:
20: e59f0028 ldr r0, [pc, #40] ; 50 <cant_inline_static+0x30>
24: e5903000 ldr r3, [r0]
28: e59f2024 ldr r2, [pc, #36] ; 54 <cant_inline_static+0x34>
2c: e5933000 ldr r3, [r3]
30: e1530002 cmp r3, r2
34: 1a000003 bne 48 <cant_inline_static+0x28>
38: e3a02001 mov r2, #1
3c: e59f3014 ldr r3, [pc, #20] ; 58 <cant_inline_static+0x38>
40: e5832000 str r2, [r3]
44: e12fff1e bx lr
48: e3a01001 mov r1, #1
4c: e12fff13 bx r3
...
58: 40002000 .word 0x40002000
0000005c <can_inline_local>:
5c: e3a02001 mov r2, #1
60: e59f3004 ldr r3, [pc, #4] ; 6c <can_inline_local+0x10>
64: e5832000 str r2, [r3]
68: e12fff1e bx lr
6c: 40002000 .word 0x40002000
Disassembly of section .text.startup:
00000000 <_GLOBAL__sub_I_cant_inline_static>:
0: e59f3014 ldr r3, [pc, #20] ; 1c <_GLOBAL__sub_I_cant_inline_static+0x1c>
4: e59f2014 ldr r2, [pc, #20] ; 20 <_GLOBAL__sub_I_cant_inline_static+0x20>
8: e2831008 add r1, r3, #8
c: e2833018 add r3, r3, #24
10: e5821008 str r1, [r2, #8]
14: e5823000 str r3, [r2]
18: e12fff1e bx lr
...
inline.o: file format elf32-littlearm
Disassembly of section .text.cant_inline_static:
00000000 <cant_inline_static>:
0: 2201 movs r2, #1
2: 4b01 ldr r3, [pc, #4] ; (8 <cant_inline_static+0x8>)
4: 601a str r2, [r3, #0]
6: 4770 bx lr
8: 40002000 .word 0x40002000
Disassembly of section .text.can_inline_local:
00000000 <can_inline_local>:
0: 2201 movs r2, #1
2: 4b01 ldr r3, [pc, #4] ; (8 <cant_inline_static+0x8>)
4: 601a str r2, [r3, #0]
6: 4770 bx lr
8: 40002000 .word 0x40002000
最佳答案
TL; DR :
用void* data;
替换void* data = 0;
。 (如果有更多的数据成员,则必须将它们中的每一个初始化为某个编译时常数值。)
完成此操作后,g++将在目标文件中预初始化derivedtype
,而不是在运行时进行初始化。
免责声明:
这不是语言律师的问题,所以我没有写语言律师的答案。以下大多数内容取决于实现,这意味着它可能不适用于与我尝试过的版本不同的任何特定编译器,版本或月相。它专门指的是GCC,尤其是ELF目标文件。它涵盖了英特尔和ARM体系结构,但我不主张对其进行概括。
C++中的静态初始化充满了(有时会说“被……困扰”)恶魔占据的细节和极端情况。下面的说明过于简单,因为(1)在这种情况下,大多数细节都无关紧要; (2)我不知道ELF加载程序的所有详细信息,尤其是在ARM平台上。但我认为它或多或少与现实相符。
静态初始化和C++标准:
就像我在上面说的那样,这不是语言律师的答案,因此我不会在标准中提供长引号。您可以阅读标准本身的§3.6.2([basic.start.init])。从本质上讲,如果初始化程序的行为良好且没有副作用,则编译器可以安排在想要的任何时候初始化全局变量,但不得迟于严格必要的时间。要明确后者,这是唯一的标准报价:
If the initialization is deferred to some point in time after the first statement of main, it shall occur before the first odr-use of any function or variable defined in the same translation unit as the variable to be initialized. (§3.6.2, para. 4).
cant_inline_static
之前看到的代码。
derivedtype
是多态类的实例非常重要。多态类的每个实例都有一个额外的隐藏数据成员,该成员包含一个指向函数指针(和其他信息) vector 的指针(“vptr”),通常称为“vtable”。这就是实现虚拟函数调用的方式:在运行时,通过对象的vtable间接调用虚拟函数。 [注1]关于这一点还有很多可以说的,但是这里的要点是,多态类的每个实例都有一个vptr,其值需要初始化。
main()
的执行开始之间,需要将各种翻译后的翻译单元(目标文件)读入内存并组合成程序镜像。在此过程中,需要为在一个翻译单元中定义并在另一翻译单元中使用的名称分配地址,并且需要在使用这些地址的位置插入这些地址。即使在一个翻译单元中,通常也需要修改对名称的引用,以考虑分配给该名称的实际地址。
-fno-zero-initialized-in-bss
)。因此,您可以观察到以下两者之间的区别:
struct S {
int one = 1;
int zeros[1000000] = {0};
};
S s;
和
struct S {
int one = 1;
int zeros[1000000];
};
S s;
在我的系统上,生成的目标文件的大小为4,000,962与2184字节。
derivedtype
,它带有一个(继承的)默认初始化的数据成员。由于它是一个多态对象的实例,因此它还具有一个内部vptr数据成员,需要对其进行初始化。因此,它看起来像一个混合数据对象,因此gcc将其放在bss节中,并在需要时插入代码以(延迟)初始化它。
derivedtype
的指针来调用虚拟成员函数。因此,从某种意义上说,如果vptr成员从未初始化过,那实际上并不重要。但是,期望编译器甚至考虑检查该场景是完全不合理的。如果创建多态类,则可能仅是因为您打算多态调用成员函数。为了确定是否可能发生多态调用而对该类的实例进行完全转义分析,几乎总是会浪费时间,因此没有理由要有人在编译器中包括该检查。 (这是个人观点。您可以不同意。:-))
derivedtype.DerivedType::VirtualMethod(p);
走得更远,您可以使用类似以下内容的方法调用不使用
this
的多态方法(即如果不是多态的则可能是
static
)摆脱困境:
((DerivedType)nullptr)->DerivedType::VirtualMethod(p);
甚至:
((decltype(derivedtype)*)(nullptr)->decltype(derivedtype)::VirtualMethod(p);
但是在您的代码中,这将无效,因为您实际上调用了
VirtualCaller
,后者显式使用了
this
。 (说实话,我不太了解那里的逻辑)。但是,上面的hack(我在代码审查中不会接受)确实避免了使用odt的
derivedtype
,从而避免了对其进行初始化。
See it here上的
Godbolt Interactive GCC compiler
.rodata
,但仍通常将其描述为“文本”节。这是我警告过份的简化之一。main()
启动之后很长一段时间。但是同样,这与这里无关。关于c++ - G++无法内联多态方法吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34357636/
我目前正在尝试让 g++ 工作,并查看 http://gcc.gnu.org/install/build.html ,我似乎找不到它在哪里说如何“执行编译器的 3 阶段 bootstrap ”。我在哪
James Powell 在他对即将举行的演示文稿的简短描述中说,他自豪地发明了最粗糙的 Python 单行代码之一: (None for g in g if (yield from g) and F
请告诉我我的证明是否正确 We have a connected graph, and specific vertex u in V(G). Suppose we compute the dfs tr
下面的test2和test3结果是不同的。 我对此感到困惑,因为它看起来像相同的逻辑,并且与linux bash ||逻辑不同。 $data = @( [PSCustomObject]@{St
我试图找到一个明确的 G 代码语法规范,而不是单个 G 代码的含义,我无处不在的规范,我的意思是详细的语法规范,目的是编写解析器。 我编写解析器没有问题,我只是在寻找语法规范,例如。我知道您不必总是为
我写了这个 mixin,但它循环了很多时间。你能帮我优化我的代码吗?或者你能建议一些其他的东西来获得想要的结果吗? dfgdfgsdfgsdf 最佳答案 希望这就是您要找的。 $spaces: (4,
默认情况下,g++ 似乎会省略未使用的类内定义方法的代码。示例 from my previous question : struct Foo { void bar() {} void baz(
是否可以将文件内容通过管道传送到 g++编译程序? 我想这样做是因为我想使用数据库中的文件而不是磁盘上的物理文件。可以通过我制作的 API 轻松检索文件内容。 例如,我想做这样的事情: g++ con
如何profile c++代码获取每行代码的调用次数和消耗时间,就像profile工具一样在 Matlab 中呢? 我尝试使用-fprofile-arcs之类的东西,但它只生成代码覆盖率报告,其中可以
如何在几行代码上禁用所有警告。可以使用 GCC 诊断功能禁用特定警告,但是否有针对所有警告的标志。我尝试了这个方法,但不起作用 #pragma GCC diagnostic push #pragma
我有一个链接到 opencv 2.2 的可执行文件。但是,我删除了 opencv 2.2 并安装了 opencv 2.3。 问题是,有没有办法在不重新编译整个源代码的情况下将这个可执行文件链接到新的共
在编译带有一些标志的以下文件时,是否可以让 g++ 显示错误? #include using namespace std; int main() { int arr[ 2 ]; cout
在学习 Haskell 时,我遇到了一个挑战,要找到两个函数 f 和 g,例如 f g 和 f 。 g 是等价的(并且是总计,因此像 f = undefined 或 f = (.) f 这样的东西不算
根据我的理解,Theta 位于 Big O 和 Omega 之间,但我看到了这个声明,但我无法理解为什么交集会出现在这里。我能否对 Θ(g(n)) = O(g(n)) ∩ Ω(g(n)) 获得数学和分
我需要为这个递归函数编写一个迭代函数。 int funcRec(int n){ if(n>1) { return 2*funcRec(n - 1) + 3*funcRec(n
我在 github repository 上有代码示例并在 travis-ci 上创建了一个构建便于复制。 最小的、完整的和可验证的例子 可能不是最小的,但我相信它足够小 它使用 boost.inte
编辑:我们将调用箭头 p纯如果存在这样的函数f即:p = arr f . 我试图更好地掌握 Haskell 中的 Arrows,我想弄清楚什么时候 f >>> (g &&& h) = (f >>> g
我有两个(或更多)函数定义为: val functionM: String => Option[Int] = s => Some(s.length) val functionM2: Int => Op
好像是的。任何直观或严肃的证据都值得赞赏。 最佳答案 没有。 我认为您的问题等同于:给定函数 f 和 g,f 是 O(g) 或 g 是 O(f) 是否总是正确的?这在 SE Computer Scie
如果我设法证明 f(n) = o(g(n))(小 o),那么这两个函数的总和 f( n) + g(n) 应该被“更大”的函数 g(n) 紧紧束缚。 然而,我在证明这一点时遇到了一些麻烦。 最佳答案 以
我是一名优秀的程序员,十分优秀!