简体   繁体   English

未使用的 C++ static 成员函数/变量未优化

[英]Unused C++ static member functions/variables not optimized out

I have enabled compiler and linker optimization in the hopes of removing all unused code/data from my ARM32 executable.我启用了编译器和 linker 优化,希望从我的 ARM32 可执行文件中删除所有未使用的代码/数据。 From my map file, I can see that the unused sections of code were indeed discarded so the optimization flags are mostly working, except for static member functions/variables of an unused class.从我的 map 文件中,我可以看到确实丢弃了未使用的代码部分,因此优化标志大部分都在工作,除了未使用的 ZA2F2ED4F8EBC2CBB4C21A29DC40AB6D 的 static 成员函数/变量之外Any ideas on how to get rid of this would be really appreciated, because this adds up to quite a bit on a resource-constrained embedded platform!任何关于如何摆脱这种情况的想法都将不胜感激,因为这在资源受限的嵌入式平台上加起来相当多!

Here's an MVCE compiled using g++ 7.5.这是使用 g++ 7.5 编译的 MVCE。 on Ubuntu 18.04在 Ubuntu 18.04

#include <string>

#include <iostream>
#include <string.h>

class unusedClass {
public:
  unusedClass() {};
  ~unusedClass() {};
  static std::string className;

  void initArray(void) {
    memset(a, 0, sizeof(a));
  }

  void printArray(void) {
    for (auto& i:a) {
      std::cout<< i << std::endl;
    }
  }

  static void printClassName(void) {
    std::cout << "This is a static member function of the class \"UNUSED CLASS\""<< std::endl;
  }

private:
  int a[1000];
};

std::string unusedClass::className = "unusedClass";

int main() {
  std::cout << "Running the Clean-up Dead Code Test" << std::endl;
  return 0;
}


Compile with optimization flags to remove unused code使用优化标志编译以删除未使用的代码

g++ -Os -flto test.cpp

To check if the static member variable was compiled into the executable检查 static 成员变量是否已编译到可执行文件中

readelf -a --wide a.out | awk '$4 == "OBJECT" { print }'
29: 0000000000201140    32 OBJECT  LOCAL  DEFAULT   24 _ZN11unusedClass9classNameB5cxx11E

As StoryTeller pointer out, _ZN11unusedClass9classNameB5cxx11E is not a member function, but the member variable static std::string className .正如 StoryTeller 指出的那样, _ZN11unusedClass9classNameB5cxx11E不是成员 function,而是成员变量static std::string className

static member variables can't just be optimized away because they could be accessed in multiple translation units. static成员变量不能仅仅优化掉,因为它们可以在多个翻译单元中访问。 They must be compiled so that the linker knows when the same static variable is used in different places.必须对它们进行编译,以便 linker 知道何时在不同的地方使用相同的 static 变量。

Using constexpr使用constexpr

If you want functions and variables to not be compiled into the executable, using constexpr instead of static often leads to this result.如果您希望函数和变量不被编译到可执行文件中,使用constexpr instead of static通常会导致此结果。 In your example, the class name is known at compile time, so using constexpr would be the more idiomatic solution anyways.在您的示例中, class 名称在编译时是已知的,因此无论如何使用constexpr将是更惯用的解决方案。

constexpr implies inline for functions and implies "inlinability" for variables. constexpr意味着函数的inline并意味着变量的“内联性”。 Since the values of constexpr variables are all known at compile time, there is no need for them to be stored in the compiled binary.由于constexpr变量的值在编译时都是已知的,因此无需将它们存储在编译后的二进制文件中。 This is why the compiler will optimize them out if unused, even on -O0 .这就是为什么编译器会在未使用时优化它们,即使在-O0上也是如此。 Here's a working example, in which the variable is not compiled: https://godbolt.org/z/bqsuTA这是一个工作示例,其中未编译变量: https://godbolt.org/z/bqsuTA

Note: You can only use const char * in a constexpr context, not std::string .注意:您只能在 constexpr 上下文中使用const char * ,而不是std::string

Edit: If you used a static const char* instead, it would also not be optimized out.编辑:如果您使用static const char*代替,它也不会被优化。 Only constexpr leads to the desired result: https://godbolt.org/z/DYNk2G只有constexpr会导致所需的结果: https://godbolt.org/z/DYNk2G

Using Anonymous namespace使用匿名namespace

See the following example:请参见以下示例:

namespace {
struct unused1 {;
  static const int x;
};
}

struct unused2 {;
  static const int x;
};

const int unused1::x = 1;
const int unused2::x = 2;

Out of the two variables .long 2 will always be found in the binary, even on -O3 .在这两个变量中, .long 2总是会在二进制文件中找到,即使在-O3上也是如此。

unused2::x:
        .long   2

Putting a class into an anonymous namespace is akin to using the static modifier for a function and makes compilation of these unused constants unnecessary.将 class 放入匿名命名空间类似于对 function 使用static修饰符,并且无需编译这些未使用的常量。

Warning: If you put this class into an anonymous namespace, different translation units will no longer use the same static variable, but their own copy of it!警告:如果你把这个 class 放到匿名命名空间中,不同的翻译单元将不再使用同一个static变量,而是他们自己的副本!

That is really strange.这真的很奇怪。 I simplified your code example and added some variables and functions to see if the observation is valid in general.我简化了您的代码示例并添加了一些变量和函数,以查看观察结果是否普遍有效。

uc.h: uc.h:

#include <string>

extern std::string mymystring1;
extern std::string mymystring2;

extern int mymyx1;
extern int mymyx2;

int mymyf1();
int mymyf

uc.cpp: uc.cpp:

#include <string>

std::string mymystring1 ="This is a very simple test what happens if fdata sections did not work!";
std::string mymystring2 ="This variable should be used";

int mymyx1 = 11111; // unused
int mymyx2 = 22222;

int mymyf1() { return 1; } // unused
int mymyf2() { return 2; }

main.cpp:主.cpp:

#include "uc.h"
int main() {
  std::cout << mymystring1 << std::endl;
  std::cout << mymyx1 << std::endl;
  std::cout << mymyf1() << std::endl;
  return 0;
}

Even if I compile and link with uc.cpp as a library, I get the unused string object linked in my code.即使我将 uc.cpp 作为库进行编译和链接,我也会在我的代码中获得未使用的字符串 object 链接。

 63: 00000000004041c0    32 OBJECT  GLOBAL DEFAULT   24 _Z11mymystring1B5cxx11
 78: 00000000004041a0    32 OBJECT  GLOBAL DEFAULT   24 _Z11mymystring2B5cxx11
 90: 0000000000404070     4 OBJECT  GLOBAL DEFAULT   23 mymyx1

Looking inside the uc.o file with objdump -h uc.o使用objdump -h uc.o uc.o 文件

11 .data.mymyx2  00000004  0000000000000000  0000000000000000  000001d0  2**2
              CONTENTS, ALLOC, LOAD, DATA
12 .data.mymyx1  00000004  0000000000000000  0000000000000000  000001d4  2**2
              CONTENTS, ALLOC, LOAD, DATA
13 .bss._Z11mymystring2B5cxx11 00000020  0000000000000000  0000000000000000  000001e0  2**5
              ALLOC
14 .bss._Z11mymystring1B5cxx11 00000020  0000000000000000  0000000000000000  000001e0  2**5
              ALLOC

We see that the data for mymyx1 and mymyx2 are in separate data sections.我们看到 mymyx1 和 mymyx2 的数据位于不同的数据部分中。 String data generates bss sections?字符串数据生成 bss 部分? And where is the data?数据在哪里?

OK, we take a look: objdump -s go OK,我们看一下: objdump -s go

Contents of section.rodata: section.rodata 的内容:

402000 62617369 635f7374 72696e67 3a3a5f4d  basic_string::_M
402010 5f636f6e 73747275 6374206e 756c6c20  _construct null 
402020 6e6f7420 76616c69 64005468 69732069  not valid.This i
402030 73206120 76657279 2073696d 706c6520  s a very simple 
402040 74657374 20776861 74206861 7070656e  test what happen
402050 73206966 20666461 74612073 65637469  s if fdata secti
402060 6f6e7320 64696420 6e6f7420 776f726b  ons did not work
402070 21005468 69732076 61726961 626c6520  !.This variable 
402080 73686f75 6c642062 65207573 656400    should be used.

If I move the second string to another file, it is removed!如果我将第二个字符串移动到另一个文件,它将被删除!

Contents of section.rodata: section.rodata 的内容:

402000 54686973 20697320 61207665 72792073  This is a very s
402010 696d706c 65207465 73742077 68617420  imple test what 
402020 68617070 656e7320 69662066 64617461  happens if fdata
402030 20736563 74696f6e 73206469 64206e6f   sections did no
402040 7420776f 726b2100                    t work!.  

For me this is simply a compiler/linker bug!对我来说,这只是一个编译器/链接器错误!

From J.Schulke answer:来自 J.Schulke 的回答:

static member variables can't just be optimized away because they could be accessed in multiple translation units. static 成员变量不能仅仅优化掉,因为它们可以在多个翻译单元中访问。 They must be compiled so that the linker knows when the same static variable is used in different places.必须对它们进行编译,以便 linker 知道何时在不同的地方使用相同的 static 变量。

They must be compiled, but they can be removed in link stage, if they are never accessed.它们必须被编译,但如果它们从未被访问过,它们可以在链接阶段被删除。 As given in the above code, functions and the integer variables are removed if not used but std::string vars are not if they are in the same file with used data.如上面的代码所示,如果不使用函数和 integer 变量,则将删除它们,但如果它们与使用的数据位于同一文件中,则不会删除std::string变量。 If we move each std::string variable in a separate file and linking via a static library, they are moved out.如果我们将每个 std::string 变量移动到单独的文件中并通过 static 库链接,它们就会被移出。 This should also be happen if we have -fdata-sections in place, but is not working here?如果我们有-fdata-sections也应该发生这种情况,但在这里不起作用? Why.为什么。 I don't know.我不知道。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM