嵌入式 C++:要不要使用 STL?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2226252/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Embedded C++ : to use STL or not?
提问by Mawg says reinstate Monica
I have always been an embedded software engineer, but usually at Layer 3 or 2 of the OSI stack. I am not really a hardware guy. I have generally always done telecoms products, usually hand/cell-phones, which generally means something like an ARM 7 processor.
我一直是一名嵌入式软件工程师,但通常在 OSI 堆栈的第 3 层或第 2 层。我不是一个真正的硬件人。我一般都做电信产品,通常是手/手机,这通常意味着类似于 ARM 7 处理器。
Now I find myself in a more generic embedded world, in a small start-up, where I might move to "not so powerful" processors (there's the subjective bit) - I cannot predict which.
现在我发现自己处于一个更通用的嵌入式世界,在一个小型初创公司中,在那里我可能会转向“不那么强大”的处理器(有主观性) - 我无法预测是哪个。
I have read quite a bit about debate about using STL in C++ in embedded systems and there is no clear cut answer. There are some small worries about portability, and a few about code size or run-time, but I have two major concerns:
1 - exception handling; I am still not sure whether to use it (see Embedded C++ : to use exceptions or not?)
2 - I strongly dislike dynamic memory allocation in embedded systems, because of the problems it can introduce. I generally have a buffer pool which is statically allocated at compile time and which serves up only fixed size buffers (if no buffers, system reset). The STL, of course, does a lot of dynamic allocation.
我已经阅读了很多关于在嵌入式系统中在 C++ 中使用 STL 的争论,但没有明确的答案。有一些关于可移植性的小问题,还有一些关于代码大小或运行时的问题,但我有两个主要问题:
1 - 异常处理;我仍然不确定是否要使用它(请参阅嵌入式 C++:是否使用异常?)
2 - 我非常不喜欢嵌入式系统中的动态内存分配,因为它会带来一些问题。我通常有一个在编译时静态分配的缓冲池,它仅提供固定大小的缓冲区(如果没有缓冲区,则系统重置)。当然,STL 做了很多动态分配。
Now I have to make the decision whether to use or forego the STL - for the whole company, for ever (it's going into some very core s/w).
现在我必须决定是使用还是放弃 STL - 为整个公司,永远(它进入一些非常核心的软件)。
Which way do I jump? Super-safe & lose much of what constitutes C++ (imo, it's more than just the language definition) and maybe run into problems later or have to add lots of exception handling & maybe some other code now?
我往哪个方向跳?超级安全并丢失了 C++ 的大部分内容(imo,它不仅仅是语言定义),并且可能以后会遇到问题,或者现在必须添加大量异常处理以及一些其他代码?
I am tempted to just go with Boost, but 1) I am not sure if it will port to every embedded processor I might want to use and 2) on their website, they say that they doesn't guarantee/recommend certain parts of it for embedded systems (especially FSMs, which seems weird). If I go for Boost & we find a problem later ....
我很想只使用Boost,但是 1) 我不确定它是否会移植到我可能想要使用的每个嵌入式处理器和 2) 在他们的网站上,他们说他们不保证/推荐它的某些部分对于嵌入式系统(尤其是 FSM,这看起来很奇怪)。如果我选择 Boost 并且我们稍后会发现问题....
采纳答案by Dan Olson
Super-safe & lose much of what constitutes C++ (imo, it's more than just the language definition) and maybe run into problems later or have to add lots of exception handling & maybe some other code now?
超级安全并丢失了 C++ 的大部分内容(imo,它不仅仅是语言定义),并且可能以后会遇到问题,或者现在必须添加大量异常处理以及一些其他代码?
We have a similar debate in the game world and people come down on both sides. Regarding the quoted part, why would you be concerned about losing "much of what constitutes C++"? If it's not pragmatic, don't use it. It shouldn't matter if it's "C++" or not.
我们在游戏世界中也有过类似的争论,双方都反对。关于引用的部分,您为什么会担心失去“构成 C++ 的大部分内容”?如果它不实用,请不要使用它。它是否是“C++”应该无关紧要。
Run some tests. Can you get around STL's memory management in ways that satisfy you? If so, was it worth the effort? A lot of problems STL and boost are designed to solve just plain don't come up if you design to avoid haphazard dynamic memory allocation... does STL solve a specific problem you face?
运行一些测试。你能以让你满意的方式绕过 STL 的内存管理吗?如果是这样,值得付出努力吗?如果您的设计是为了避免随意的动态内存分配,那么 STL 和 boost 旨在解决的很多问题就不会出现……STL 是否解决了您面临的特定问题?
Lots of people have tackled STL in tight environments and been happy with it. Lots of people just avoid it. Some people propose entirely new standards. I don't think there's one right answer.
很多人都在紧张的环境中处理过 STL 并对此感到满意。很多人只是避免它。有些人提出了全新的标准。我认为没有一个正确的答案。
回答by Brian Neal
I work on real-time embedded systems every day. Of course, my definition of embedded system may be different than yours. But we make full use of the STL and exceptions and do not experience any unmanageable problems. We also make use of dynamic memory (at a very high rate; allocating lots of packets per second, etc.) and have not yet needed to resort to any custom allocators or memory pools. We have even used C++ in interrupt handlers. We don't use boost, but only because a certain government agency won't let us.
我每天都在研究实时嵌入式系统。当然,我对嵌入式系统的定义可能与你的不同。但是我们充分利用了 STL 和异常,并没有遇到任何无法管理的问题。我们还使用了动态内存(以非常高的速率;每秒分配大量数据包等)并且还不需要求助于任何自定义分配器或内存池。我们甚至在中断处理程序中使用了 C++。我们不使用 boost,只是因为某个政府机构不让我们使用。
It is our experience you can indeed use many modern C++ features in an embedded environment as long as you use your head and conduct your own benchmarks. I highly recommend you make use of Scott Meyer's Effective C++3rd edition as well as Sutter and Alexandrescu's C++ Coding Standardsto assist you in using C++ with a sane programming style.
根据我们的经验,您确实可以在嵌入式环境中使用许多现代 C++ 功能,只要您发挥自己的头脑并进行自己的基准测试。我强烈建议您使用 Scott Meyer 的Effective C++3rd edition 以及 Sutter 和 Alexandrescu 的C++ 编码标准来帮助您以理智的编程风格使用 C++。
Edit: After getting an upvote on this 2 years later, let me post an update. We are much farther along in our development and we have finally hit spots in our code where the standard library containers are too slow under high performance conditions. Here we did in fact resort to custom algorithms, memory pools, and simplified containers. That is the beauty of C++ though, you can use the standard library and get all the good things it provides for 90% of your use cases. You don't throw it all out when you meet problems, you just hand-optimize the trouble spots.
编辑:在 2 年后获得赞成票后,让我发布更新。我们在我们的开发中走得更远,我们终于在我们的代码中找到了标准库容器在高性能条件下太慢的地方。在这里,我们确实使用了自定义算法、内存池和简化的容器。不过,这就是 C++ 的美妙之处,您可以使用标准库并获得它为 90% 的用例提供的所有好处。当你遇到问题时,你不会把它全部扔掉,你只是手动优化问题点。
回答by j_random_hacker
The other posts have addressed the important issues of dynamic memory allocation, exceptions and possible code bloat. I just want to add: Don't forget about <algorithm>
! Regardless of whether you use STL vectors or plain C arrays and pointers, you can still use sort()
, binary_search()
, random_shuffle()
, the functions for building and managing heaps, etc. These routines will almost certainly be faster and less buggy than versions you build yourself.
其他帖子解决了动态内存分配、异常和可能的代码膨胀等重要问题。我只想补充:不要忘记<algorithm>
!无论您使用 STL 向量还是普通的 C 数组和指针,您仍然可以使用sort()
, binary_search()
, random_shuffle()
,用于构建和管理堆的函数等。这些例程几乎肯定会比您自己构建的版本更快,错误更少。
Example: unless you think about it carefully, a shuffle algorithm you build yourself is likely to produce skewed distributions; random_shuffle()
won't.
示例:除非您仔细考虑,否则您自己构建的 shuffle 算法很可能会产生偏斜分布;random_shuffle()
惯于。
回答by Crashworks
Paul Pedriana from Electronic Arts wrote in 2007 a lengthy treatiseon why the STL was inappropriate for embedded console development and why they had to write their own. It's a detailed article, but the most important reasons were:
来自 Electronic Arts 的 Paul Pedriana 在 2007 年写了一篇冗长的论文,阐述了为什么 STL 不适合嵌入式控制台开发以及为什么他们必须自己编写。这是一篇详细的文章,但最重要的原因是:
- STL allocators are slow, bloated, and inefficient
- Compilers aren't actually very good at inlining all those deep function calls
- STL allocators don't support explicit alignment
- The STL algorithms that come with GCC and MSVC's STL aren't very performant, because they're very platform-agnostic and thus miss a lot of microoptimizations that can make a big difference.
- STL 分配器缓慢、臃肿且效率低下
- 编译器实际上并不擅长内联所有这些深层函数调用
- STL 分配器不支持显式对齐
- GCC 和 MSVC 的 STL 附带的 STL 算法的性能不是很好,因为它们与平台无关,因此错过了许多可以产生重大影响的微优化。
Some years ago, our company made the decision not to use the STL at all, instead implementing our own system of containers that are maximally performant, easier to debug, and more conservative of memory. It was a lot of work but it has repaid itself many times over. But ours is a space in which products compete on how much they can cram into 16.6ms with a given CPU and memory size.
几年前,我们公司决定完全不使用 STL,而是实施我们自己的容器系统,这些系统具有最高性能、更易于调试和更节省内存。这是很多工作,但它已经多次回报了自己。但在我们的领域,在给定 CPU 和内存大小的情况下,产品会在 16.6 毫秒内进行竞争。
As to exceptions: they are slowon consoles, and anyone who tells you otherwise hasn't tried timing them. Simply compiling with them enabled will slow down the entire program because of the necessary prolog/epilog code -- measure it yourself if you don't believe me. It's even worse on in-order CPUs than it is on the x86. For this reason, the compiler we use doesn't even support C++ exceptions.
至于例外:它们在控制台上很慢,任何告诉你否则的人都没有尝试过计时。由于必要的序言/结语代码,简单地在启用它们的情况下进行编译会减慢整个程序的速度——如果您不相信我,请自行测量。在有序 CPU 上的情况甚至比在 x86 上还要糟糕。因此,我们使用的编译器甚至不支持 C++ 异常。
The performance gain isn't so much from avoiding the cost of an exception throw — it's from disabling exceptions entirely.
性能提升并不是因为避免了异常抛出的成本——而是完全禁用异常。
回答by Mark Ransom
Let me start out by saying I haven't done embedded work for a few years, and never in C++, so my advice is worth every penny you're paying for it...
首先让我说我有几年没有做过嵌入式工作,而且从来没有做过 C++,所以我的建议值得你为此付出的每一分钱......
The templates utilized by STL are never going to generate code you wouldn't need to generate yourself, so I wouldn't worry about code bloat.
STL 使用的模板永远不会生成您不需要自己生成的代码,所以我不会担心代码膨胀。
The STL doesn't throw exceptions on its own, so that shouldn't be a concern. If your classes don't throw, you should be safe. Divide your object initialization into two parts, let the constructor create a bare bones object and then do any initialization that could fail in a member function that returns an error code.
STL 本身不会抛出异常,因此不必担心。如果你的课程没有抛出,你应该是安全的。将您的对象初始化分为两部分,让构造函数创建一个裸骨对象,然后执行任何可能在返回错误代码的成员函数中失败的初始化。
I think all of the container classes will let you define your own allocation function, so if you want to allocate from a pool you can make it happen.
我认为所有的容器类都可以让你定义自己的分配函数,所以如果你想从池中分配,你可以实现它。
回答by thinwybk
The open source project "Embedded Template Library (ETL)"targets the usual problems with the STL used in Embedded Applications by providing/implementing a library:
开源项目“嵌入式模板库 (ETL)”通过提供/实现一个库来解决嵌入式应用程序中使用的 STL 的常见问题:
- deterministic behaviour
- "Create a set of containers where the size or maximum size is determined at compile time. These containers should be largely equivalent to those supplied in the STL, with a compatible API."
- no dynamic memory allocation
- no RTTI required
- little use of virtual functions (only when absolutely necessary)
- set of fixed capacity containers
- cache friendly storage of containers as continously allocated memory block
- reduced container code size
- typesafe smart enumerations
- CRC calculations
- checksums & hash functions
- variants = sort of type safe unions
- Choice of asserts, exceptions, error handler or no checks on errors
- heavily unit tested
- well documented source code
- and other features...
- 确定性行为
- “创建一组在编译时确定大小或最大大小的容器。这些容器应该在很大程度上等同于 STL 中提供的容器,并具有兼容的 API。”
- 没有动态内存分配
- 不需要 RTTI
- 很少使用虚函数(仅在绝对必要时)
- 一套固定容量的容器
- 缓存友好的容器存储作为连续分配的内存块
- 减少容器代码大小
- 类型安全智能枚举
- CRC计算
- 校验和和哈希函数
- 变体 = 某种类型安全联合
- 选择断言、异常、错误处理程序或不检查错误
- 大量单元测试
- 有据可查的源代码
- 和其他功能...
You can also consider a commercial C++ STL for Embedded Developersprovided by E.S.R. Labs.
回答by ddh
for memory management, you can implement your own allocator, which request memory from the pool. And all STL container have a template for the allocator.
for exception, STL doesn't throw many exceptions, in generally, the most common are: out of memory, in your case, the system should reset, so you can do reset in the allocator. others are such as out of range, you can avoid it by the user.
so, i think you can use STL in embedded system :)
对于内存管理,您可以实现自己的分配器,从池中请求内存。并且所有 STL 容器都有一个分配器模板。
对于异常,STL 不会抛出很多异常,一般来说,最常见的是:内存不足,在您的情况下,系统应该重置,因此您可以在分配器中进行重置。其他如超出范围,您可以由用户避免。
所以,我认为你可以在嵌入式系统中使用 STL :)
回答by Alexander Poluektov
In addition to all comments, I would propose you reading of Technical Report on C++ Performancewhich specifically addresses topics that you are interested in: using C++ in embedded (including hard real-time systems); how exception-handling usually implemented and which overhead it has; free store allocation's overhead.
除了所有评论之外,我还建议您阅读有关 C++ 性能的技术报告,其中专门针对您感兴趣的主题:在嵌入式(包括硬实时系统)中使用 C++;通常如何实现异常处理以及它有哪些开销;免费存储分配的开销。
The report is really good as is debunks many popular tails about C++ performance.
该报告非常好,因为它揭穿了许多关于 C++ 性能的流行尾巴。
回答by piotr
It basically depends on your compiler and in the amount of memory you have. If you have more than a few Kb of ram, having dynamic memory allocation helps a lot. If the implementation of malloc from the standard library that you have is not tuned to your memory size you can write your own, or there are nice examples around such as mm_malloc from Ralph Hempelthat you can use to write your new and delete operators on top.
它基本上取决于您的编译器和您拥有的内存量。如果你有超过几 Kb 的内存,动态内存分配会有很大帮助。如果您拥有的标准库中的 malloc 实现没有调整到您的内存大小,您可以编写自己的,或者有很好的例子,例如来自 Ralph Hempel 的 mm_malloc,您可以使用它来编写您的 new 和 delete 运算符.
I don't agree with those that repeat the meme that exceptions and stl containers are too slow, or too bloated etc. Of course it adds a little more code than a simple C's malloc, but judicious use of exceptions can make code much clear and avoid too much error checking blurb in C.
我不同意那些重复异常和 stl 容器太慢或太臃肿等模因的人。当然它比简单的 C 的 malloc 添加了更多的代码,但明智地使用异常可以使代码更加清晰和清晰避免在 C 中进行过多的错误检查。
One has to keep in mind that STL allocators will increase their allocations in powers of two, which means sometimes it will do some reallocations until it reaches the correct size, which you can prevent with reserveso it becomes as cheap as one malloc of the desired size if you know the size to allocate anyway.
必须记住,STL 分配器会以 2 的幂增加它们的分配,这意味着有时它会进行一些重新分配,直到达到正确的大小,您可以通过保留来防止这种情况发生,因此它变得与所需的 malloc 一样便宜size 如果您知道无论如何要分配的大小。
If you have a big buffer in a vector for example, at some point it might do a reallocation and ends using up 1.5x the memory size that you are intending it to use at some point while reallocating and moving data. (For example, at some point it has N bytes allocated, you add data via append or an insertion iterator and it allocates 2N bytes, copies the first N and releases N. You have 3N bytes allocated at some point).
例如,如果您在向量中有一个大缓冲区,在某些时候它可能会进行重新分配,并最终使用的内存大小是您在重新分配和移动数据时打算在某个时候使用的内存大小的 1.5 倍。(例如,在某些时候它分配了 N 个字节,您通过附加或插入迭代器添加数据,它分配 2N 个字节,复制前 N 个并释放 N 个。在某个时候您分配了 3N 个字节)。
So in the end it has a lot of advantages, and pays of if you know what you are doing. You should know a little of how C++ works to use it on embedded projects without surprises.
所以最终它有很多优点,如果你知道自己在做什么,就会付出代价。您应该对 C++ 的工作原理有所了解,以便在嵌入式项目中毫无意外地使用它。
And to the guy of the fixed buffers and reset, you can always reset inside the new operator or whatever if you are out of memory, but that would mean you did a bad design that can exhaust your memory.
对于固定缓冲区和重置的人来说,你总是可以在 new 操作符内部重置,或者如果你的内存不足,但这意味着你做了一个可能耗尽你的内存的糟糕设计。
An exception being thrown with ARM realview 3.1:
ARM realview 3.1 引发的异常:
--- OSD\#1504 throw fapi_error("OSDHANDLER_BitBlitFill",res);
S:218E72F0 E1A00000 MOV r0,r0
S:218E72F4 E58D0004 STR r0,[sp,#4]
S:218E72F8 E1A02000 MOV r2,r0
S:218E72FC E24F109C ADR r1,{pc}-0x94 ; 0x218e7268
S:218E7300 E28D0010 ADD r0,sp,#0x10
S:218E7304 FA0621E3 BLX _ZNSsC1EPKcRKSaIcE <0x21a6fa98>
S:218E7308 E1A0B000 MOV r11,r0
S:218E730C E1A0200A MOV r2,r10
S:218E7310 E1A01000 MOV r1,r0
S:218E7314 E28D0014 ADD r0,sp,#0x14
S:218E7318 EB05C35F BL fapi_error::fapi_error <0x21a5809c>
S:218E731C E3A00008 MOV r0,#8
S:218E7320 FA056C58 BLX __cxa_allocate_exception <0x21a42488>
S:218E7324 E58D0008 STR r0,[sp,#8]
S:218E7328 E28D1014 ADD r1,sp,#0x14
S:218E732C EB05C340 BL _ZN10fapi_errorC1ERKS_ <0x21a58034>
S:218E7330 E58D0008 STR r0,[sp,#8]
S:218E7334 E28D0014 ADD r0,sp,#0x14
S:218E7338 EB05C36E BL _ZN10fapi_errorD1Ev <0x21a580f8>
S:218E733C E51F2F98 LDR r2,0x218e63ac <OSD\#1126>
S:218E7340 E51F1F98 LDR r1,0x218e63b0 <OSD\#1126>
S:218E7344 E59D0008 LDR r0,[sp,#8]
S:218E7348 FB056D05 BLX __cxa_throw <0x21a42766>
Doesn't seem so scary, and no overhead is added inside {} blocks or functions if the exception isn't thrown.
看起来并不那么可怕,如果没有抛出异常,也不会在 {} 块或函数内添加任何开销。
回答by Edan Maor
The biggest problem with STL in embedded systems is the memory allocation issue (which, as you said, causes a lot of problems).
嵌入式系统中 STL 的最大问题是内存分配问题(正如你所说,这会导致很多问题)。
I'd seriously research creating your own memory management, built by overriding the new/delete operators. I'm pretty sure that with a bit of time, it can be done, and it's almost certainly worth it.
我会认真研究创建自己的内存管理,通过覆盖新的/删除操作符来构建。我很确定,只要花一点时间,它就可以完成,而且几乎肯定是值得的。
As for the exceptions issue, I wouldn't go there. Exceptions are a serious slowdownof your code, because they cause every single block ({ }
) to have code before and after, allowing the catching of the exception and the destruction of any objects contained within. I don't have hard data on this on hand, but every time I've seen this issue come up, I've seen overwhelming evidence of a massive slowdown caused by using exceptions.
至于例外问题,我不会去那里。异常会严重拖慢您的代码,因为它们会导致每个块 ( { }
) 前后都有代码,从而允许捕获异常并销毁其中包含的任何对象。我手头没有这方面的硬数据,但是每次我看到这个问题出现时,我都会看到大量证据表明使用异常会导致速度大幅下降。
Edit:
Since a lot of people wrote comments stating that exception handling is notslower, I thought I'd add this little note (thanks for the people who wrote this in comments, I thought it'd be good to add it here).
编辑:
由于很多人写了评论说异常处理并不慢,我想我会添加这个小注释(感谢在评论中写下这个的人,我认为在这里添加它会很好)。
The reason exception handling slows down your code is because the compiler must make sure that every block ({}
), from the place an exception is thrown to the place it is dealt with, must deallocate any objects within it. This is code that is added to every block, regardless of whether anyone ever throws an exception or not (since the compiler can't tell at compile time whether this block will be part of an exception "chain").
异常处理减慢代码速度的原因是编译器必须确保每个块 ( {}
),从抛出异常的位置到处理异常的位置,都必须释放其中的所有对象。这是添加到每个块的代码,无论是否有人抛出异常(因为编译器在编译时无法判断此块是否将成为异常“链”的一部分)。
Of course, this might be an old way of doing things that has gotten much faster in newer compilers (I'm not exactly up-to-date on C++ compiler optimizations). The best way to know is just to run some sample code, with exceptions turned on and off (and which includes a few nested functions), and time the difference.
当然,这可能是一种在较新的编译器中变得更快的旧方法(我并不完全了解 C++ 编译器优化)。最好的了解方法是运行一些示例代码,打开和关闭异常(包括一些嵌套函数),并计时差异。