简体   繁体   English

Microblaze & C++ | 为什么在某些条件下代码大小会急剧增加?

[英]Microblaze & C++ | Why does the code size increase dramatically under certain conditions?

I have been developing embedded software for the Microblaze processor for more than a year using C++.一年多来,我一直在使用 C++ 为 Microblaze 处理器开发嵌入式软件。 My designs were not so complex, so I wasn't using the powerful, object-oriented features of the language.我的设计并没有那么复杂,所以我没有使用该语言强大的、面向对象的特性。

For a while, I have been trying to enhance the structure of my designs.有一段时间,我一直在尝试增强我的设计结构。 For this purpose, I try to widely use the sophisticated features of C++ such as inheritance, polymorphism, etc. As a newbie, I believe that using inheritance solely doesn't affect the code size.为此,我尝试广泛使用 C++ 的复杂功能,例如 inheritance、多态性等。作为新手,我相信仅使用 inheritance 不会影响代码的大小。 Only the polymorphism has some side effects like adding virtual table pointers, run-time-type-informations, etc. My problem started with adding a pure virtual member function to a base class.只有多态性有一些副作用,比如添加虚拟表指针、运行时类型信息等。我的问题始于将纯虚拟成员 function 添加到基础 class 中。

To provide a runnable example, I will try to mimic the situation that I face against.为了提供一个可运行的示例,我将尝试模仿我所面临的情况。

The code below compiles and produces 13292 bytes of code.下面的代码编译并生成13292 字节的代码。 There is no way that this code can have such an amount of instructions.这段代码不可能有这么多的指令。 But, I believe that there are some parts from the generated BSP that are mandatory to include when producing an elf file.但是,我相信生成的 BSP 中的某些部分是在生成 elf 文件时必须包含的。

class Base{
public:
    Base() = default;
    ~Base() = default;
  
    virtual void func() {}
  
    int m_int;
};

class Derived : public Base{
public:
    Derived() = default;
    ~Derived() = default;
    
    void func() final {}
  
    int m_int2;
};

int main()
{
    Derived d;
  
    while(1);    
}
 

13KB is not that much when you think that you have nearly 128KB of usable RAM.当您认为您有近 128KB 的可用 RAM 时, 13KB并不算多。 Actually, I didn't even notice the size of the produced code until the problem with the pure virtual functions emerges.实际上,直到出现纯虚函数的问题时,我才注意到生成的代码的大小。 The second code, below, has the same structure except for the func() is now a pure virtual function.下面的第二个代码具有相同的结构,只是func()现在是纯虚拟 function。 Building this code gives us a code size which more than the available*(128KB)* RAM size.构建此代码为我们提供了一个大于可用*(128KB)* RAM 大小的代码大小。 So, I modified the linker file to add some fake RAM just to be able to compile the code.因此,我修改了 linker 文件以添加一些假 RAM,以便能够编译代码。 After a successful compilation, the size of the produced code is nearly 157KB!编译成功后,生成的代码大小接近157KB!

class Base{
public:
    Base() = default;
    ~Base() = default;
  
    virtual void func() = 0;
  
    int m_int;
};

class Derived : public Base{
public:
    Derived() = default;
    ~Derived() = default;
    
    void func() final {}
  
    int m_int2;
};

int main()
{
    Derived d;
  
    while(1);    
}

I didn't change any preferences of the compiler, all arguments are in their default states.我没有更改编译器的任何首选项,所有 arguments 都处于默认状态。 There are no additional libraries other than the auto-generated ones.除了自动生成的库之外,没有其他库。 What do you think that the problem could be?你认为问题可能是什么?

Some Additional Notes一些附加说明

  • I tried the codes on two different IDEs.我在两个不同的 IDE 上尝试了代码。 Vivado SDK 2017.2 and Vitis 2019.2 Vivado SDK 2017.2 和 Vitis 2019.2
  • The same problem also goes for the dynamic allocation calls(operator new and delete).同样的问题也适用于动态分配调用(操作员 new 和 delete)。 Replacing them with C-Style malloc and free solves the problem.用 C 型 malloc 替换它们并免费解决问题。
  • Building the codes in the release mode solves the problem also.以发布模式构建代码也解决了这个问题。 In release mode, the produced code is 1900 bytes whether I use the pure virtual function or not.在发布模式下,无论我是否使用纯虚拟 function,生成的代码都是 1900 字节。

I can provide additional information if needed, thanks如果需要,我可以提供更多信息,谢谢

I asked the same question on Xilinx forums, you can find it here我在赛灵思论坛上问过同样的问题,你可以在这里找到

The solution is a little bit creepy:) Before beginning, special thanks to everyone who helped.解决方案有点令人毛骨悚然:) 在开始之前,特别感谢所有提供帮助的人。

SHORT ANSWER简短的回答

Just add the following code piece to your main file:只需将以下代码段添加到您的主文件中:

extern "C" void __cxa_pure_virtual() { while(1); }

If you also want to solve the problem related to operator new and operator delete , add the following codes also:如果你还想解决operator newoperator delete的问题,还可以添加以下代码:

void* operator new(const std::size_t size) noexcept
{
    void* p = std::malloc(size);
    return p;
}

void operator delete(void* p) noexcept
{
    std::free(p);
}

DETAILS细节

The original solution is here .原来的解决方案在这里 The problem starts with completely pulling libstdc++ out of the picture.问题始于将libstdc++完全排除在外。 This way we waive the right of using standard library functions, so we should provide our own implementations of the standard calls such as malloc , new , free , etc. Even if you reimplement all required calls, the compiler would complain about the lack of a function called __cxa_pure_virtual() .这样我们就放弃了使用标准库函数的权利,所以我们应该提供我们自己的标准调用的实现,例如mallocnewfree等。即使你重新实现所有必需的调用,编译器也会抱怨缺少一个function 称为__cxa_pure_virtual() This is a clue for the final solution.这是最终解决方案的线索。

The __cxa_pure_virtual function is an error handler that is invoked when a pure virtual function is called. __cxa_pure_virtual function 是一个错误处理程序,在调用纯虚拟 function 时调用。 We can easily say that we never make such foolish attempts.我们可以很容易地说,我们从来没有做过这种愚蠢的尝试。 But, the compiler never trusts any software developer:) Therefore, when you write a C++ code that includes pure virtual functions, the compiler implicitly adds an error handler to handle potential runtime errors.但是,编译器从不信任任何软件开发人员:) 因此,当您编写包含纯虚函数的 C++ 代码时,编译器会隐式添加错误处理程序来处理潜在的运行时错误。 As you can guess that those are expensive calls for systems with limited resources such as in our case the Microblaze.正如您可以猜到的那样,对于资源有限的系统(例如我们的 Microblaze)来说,这些都是昂贵的调用。

So, if we are writing a C++ application that has pure virtual functions we shall supply our own __cxa_pure_virtual error handler function.因此,如果我们正在编写一个具有纯虚函数的 C++ 应用程序,我们将提供我们自己的__cxa_pure_virtual错误处理程序 function。 If you are not a competitive embedded software developer you should just add an endless to your custom handler function.如果您不是一个有竞争力的嵌入式软件开发人员,您应该在您的自定义处理程序 function 中添加一个无尽的。 Don't worry, you will never have a chance to call your pure virtual function that invokes the error handler as long as you follow the best practices of the language.别担心,只要您遵循语言的最佳实践,您将永远没有机会调用调用错误处理程序的纯虚拟 function。

The problem with the operator new and operator delete is also related to underlying exception mechanisms. operator newoperator delete的问题也与底层异常机制有关。 To avoid expensive exception handling mechanisms you could just reimplement them in a way that doesn't throw any exception.为了避免昂贵的异常处理机制,您可以以不引发任何异常的方式重新实现它们。 The only thing that you should consider is to check the allocation success after calling the operator new as it will no more produce exceptions.您应该考虑的唯一一件事是在调用operator new后检查分配是否成功,因为它不会再产生异常。 I believe that you will never need to call the operator delete as long as you work on an operating systemless application project.我相信只要你在一个无操作系统的应用程序项目上工作,你永远不需要调用operator delete

After applying this holy recipe on your own codes you will see that the size of the executable file will fall back to its original state.在你自己的代码上应用这个神圣的秘诀后,你会看到可执行文件的大小将回落到它原来的 state。

The answer is open to contributions and suggestions.答案对贡献和建议开放。 I would be appreciated if you could make so如果你能做到,我将不胜感激

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

相关问题 WINAPI C / C ++ - >为什么二进制大小会急剧增加? (从VS2013切换到VS 2015) - WINAPI C/C++ -> why did the binary size increase dramatically ? (Switch from VS2013 to VS 2015) 与at()或索引相比,为什么使用C ++迭代器会大大增加代码大小? - Why does using C++ iterators increase code size drastically compared to at() or indexing? 为什么添加虚拟方法会增加C ++中的类大小? - Why does adding virtual method increase class size in C++? MicroBlaze上C ++的线程安全性 - Thread safety of C++ on MicroBlaze 为什么生成 pdb 文件会增加我的原生 C++ exe 的大小? - Why does generating pdb files increase the size of my native C++ exe? MSVC C++ 编译器在什么情况下有时会在 function 运算符 new[] 返回的指针之前直接写入数组大小? - Under what conditions does MSVC C++ Compiler sometimes write the array size directly before the pointer returned from function operator new[]? 在什么条件下C ++优化构造函数调用? - Under what conditions does C++ optimize out constructor calls? 为什么C ++中的析构函数会增加它们所在对象的大小? - Why do destructors in C++ increase the size of the object they're in? 用c ++增加堆栈大小 - Increase stack size in c++ c++ 增加缓冲区大小 - c++ increase buffer size
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM