嵌入式 C++ : 是否使用STL?
我一直是一名嵌入式软件工程师,但通常在 OSI 堆栈的第 3 层或第 2 层工作。我并不是一个真正的硬件专家。我一般都做电信产品,通常是手机/手机,这通常意味着像 ARM 7 处理器之类的东西。
现在我发现自己处于一个更通用的嵌入式世界中,在一家小型初创公司中,我可能会转向“不太强大”的处理器(有主观的部分) - 我无法预测哪个。
我读过很多关于在嵌入式系统中使用 C++ 中的 STL 的争论,但没有明确的答案。对于可移植性有一些小担忧,还有一些关于代码大小或运行时的担忧,但我有两个主要担忧:
1——异常处理;我仍然不确定是否使用它(请参阅 嵌入式 C++ :使用异常或不是吗?)
2 - 我非常不喜欢嵌入式系统中的动态内存分配,因为它会带来问题。我通常有一个缓冲池,它在编译时静态分配,并且仅提供固定大小的缓冲区(如果没有缓冲区,则系统重置)。当然,STL 做了很多动态分配。
现在我必须决定是否使用或放弃 STL - 为了整个公司,永远(它会进入一些非常核心的软件)。
我该往哪个方向跳?超级安全&失去了 C++ 的大部分内容(在我看来,它不仅仅是语言定义),并且以后可能会遇到问题,或者必须添加大量异常处理和处理。也许现在还有其他代码?
我很想选择 Boost,但是 1)我不确定它是否会移植到每个嵌入式处理器我可能想在他们的网站上使用 和 2),他们说他们不保证/推荐嵌入式系统的某些部分(尤其是 FSM,这看起来很奇怪)。如果我选择Boost &后来我们发现一个问题......
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.
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.
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).
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?
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 ....
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(12)
我每天都在研究实时嵌入式系统。当然,我对嵌入式系统的定义可能和你的不同。但我们充分利用了STL和异常,并没有遇到任何无法管理的问题。我们还使用动态内存(以非常高的速率;每秒分配大量数据包等),并且还不需要诉诸任何自定义分配器或内存池。我们甚至在中断处理程序中使用了 C++。我们不使用Boost,只是因为某个政府机构不允许我们使用。
根据我们的经验,只要您动动脑筋并进行自己的基准测试,您确实可以在嵌入式环境中使用许多现代 C++ 功能。我强烈建议您使用 Scott Meyer 的Effective C++第三版以及 Sutter 和 Alexandrescu 的C++ 编码标准来帮助您以合理的编程风格使用 C++。
编辑:两年后获得赞成票后,让我发布更新。我们的开发已经走得更远了,我们终于在代码中找到了标准库容器在高性能条件下太慢的地方。在这里,我们实际上求助于自定义算法、内存池和简化的容器。但这就是 C++ 的美妙之处,您可以使用标准库并获得它为 90% 的用例提供的所有好东西。当你遇到问题时,你不会把所有的东西都扔掉,你只是手动优化有问题的地方。
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.
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 Standards to assist you in using C++ with a sane programming style.
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.
我们在游戏界也有过类似的争论,双方都有不同意见。关于引用的部分,为什么您会担心失去“构成 C++ 的大部分内容”?如果不实用,请不要使用它。是否是“C++”并不重要。
运行一些测试。您能以令您满意的方式绕过 STL 的内存管理吗?如果是这样,值得付出努力吗?如果您的设计是为了避免随意的动态内存分配,STL 和 boost 的很多问题都是为了简单地解决而设计的,但这些问题不会出现……STL 是否解决了您面临的特定问题?
很多人都在紧张的环境中处理过 STL,并且对此感到满意。很多人只是回避它。有些人提出全新标准。我认为没有一个正确的答案。
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.
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?
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 向量还是纯 C 数组和指针,您仍然可以使用sort()
、binary_search()
、random_shuffle()
、用于构建和管理堆的函数等。这些例程几乎肯定会比您自己构建的版本更快且错误更少。示例:除非您仔细考虑,否则您自己构建的洗牌算法 可能会产生偏态分布;
random_shuffle()
不会。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 usesort()
,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.Example: unless you think about it carefully, a shuffle algorithm you build yourself is likely to produce skewed distributions;
random_shuffle()
won't.来自 Electronic Arts 的 Paul Pedriana 在 2007 年写了一篇长篇文章论述为什么 STL 不适合嵌入式控制台开发以及为什么他们必须编写自己的 STL。这是一篇详细的文章,但最重要的原因是:
而且效率低下
几年前,我们公司决定根本不使用 STL,而是实施我们自己的容器系统,该系统具有最高的性能、更易于调试且更节省内存。这是一项艰巨的工作,但它已经得到了很多倍的回报。但我们的领域是一个产品竞争的领域,在给定的 CPU 和内存大小下,它们可以在 16.6 毫秒内塞进多少时间。
至于例外情况:它们在控制台上很慢,任何告诉你其他情况的人都没有'我尝试给他们计时。由于必要的序言/结尾代码,简单地在启用它们的情况下进行编译会减慢整个程序的速度——如果您不相信我,请自行测量。在有序 CPU 上,情况甚至比在 x86 上更糟糕。因此,我们使用的编译器甚至不支持 C++ 异常。
性能提升并不在于避免异常抛出的成本,而是在于完全禁用异常。
Paul Pedriana from Electronic Arts wrote in 2007 a lengthy treatise on 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:
and inefficient
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.
As to exceptions: they are slow on 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.
The performance gain isn't so much from avoiding the cost of an exception throw — it's from disabling exceptions entirely.
首先我要说的是,我已经好几年没有做过嵌入式工作了,而且从来没有用过 C++,所以我的建议值得你为此付出的每一分钱……
STL 使用的模板永远不会生成代码你不需要自己生成,所以我不用担心代码膨胀。
STL 本身不会引发异常,因此不必担心。如果你的课程没有抛出异常,那么你应该是安全的。将对象初始化分为两部分,让构造函数创建一个简单的对象,然后在返回错误代码的成员函数中执行可能失败的任何初始化。
我认为所有容器类都会让您定义自己的分配函数,因此如果您想从池中分配,您可以实现它。
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...
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.
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.
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.
开源项目“嵌入式模板库 (ETL)” 针对嵌入式应用程序中使用的 STL 的常见问题,通过提供/实现库:
您还可以考虑商业面向嵌入式开发人员的 C++ STL 由 ESR 实验室提供。
The open source project "Embedded Template Library (ETL)" targets the usual problems with the STL used in Embedded Applications by providing/implementing a library:
You can also consider a commercial C++ STL for Embedded Developers provided by E.S.R. Labs.
对于内存管理,您可以实现自己的分配器,它从池中请求内存。所有STL容器都有一个分配器模板。
对于异常,STL不会抛出很多异常,一般来说,最常见的是:内存不足,在你的情况下,系统应该重置,所以你可以在分配器中重置。其他的比如超出范围,你可以由用户避免。
所以,我认为你可以在嵌入式系统中使用STL:)
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 :)
除了所有评论之外,我建议您阅读技术报告关于 C++ 性能,专门讨论您感兴趣的主题:在嵌入式(包括硬实时系统)中使用 C++;异常处理通常是如何实现的以及它有哪些开销;免费存储分配的开销。
该报告非常好,因为它揭穿了许多有关 C++ 性能的流行说法。
In addition to all comments, I would propose you reading of Technical Report on C++ Performance which 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.
The report is really good as is debunks many popular tails about C++ performance.
它基本上取决于您的编译器和您拥有的内存量。如果您有超过几 Kb 的内存,动态内存分配会很有帮助。如果您拥有的标准库中的 malloc 实现未根据您的内存大小进行调整,您可以编写自己的内存大小,或者有一些很好的示例,例如 来自 Ralph Hempel 的 mm_malloc,您可以使用它在顶部编写新的和删除的运算符。
我不同意那些重复这种说法的人,即异常和 stl 容器太慢或太臃肿等。当然,它比简单的 C 的 malloc 添加了更多的代码,但明智地使用异常可以使代码更加清晰和清晰。避免在 C 中进行过多的错误检查。
必须记住,STL 分配器将以 2 的幂增加其分配,这意味着有时它会进行一些重新分配,直到达到正确的大小,您可以使用 来防止这种情况Reserve 因此,如果您知道要分配的大小,那么它就变得与所需大小的 malloc 一样便宜。
例如,如果向量中有一个大缓冲区,则在某些时候它可能会进行重新分配,并最终使用您在重新分配和移动数据时打算在某个时刻使用的内存大小的 1.5 倍。 (例如,在某个时刻它分配了 N 个字节,您通过追加或插入迭代器添加数据,它会分配 2N 个字节,复制前 N 个字节并释放 N 个。在某个时刻您分配了 3N 个字节)。
所以最终它有很多优点,如果你知道自己在做什么的话,它是值得的。您应该了解一点 C++ 的工作原理,以便在嵌入式项目中使用它而不会有任何意外。
对于固定缓冲区和重置的人来说,如果内存不足,您始终可以在新运算符或其他内容中重置,但这意味着您做了一个糟糕的设计,可能会耗尽您的内存。
ARM realview 3.1 抛出异常:
看起来并不那么可怕,如果不抛出异常,则不会在 {} 块或函数内添加任何开销。
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 Hempel that you can use to write your new and delete operators on top.
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.
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 reserve so it becomes as cheap as one malloc of the desired size if you know the size to allocate anyway.
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).
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.
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.
An exception being thrown with ARM realview 3.1:
Doesn't seem so scary, and no overhead is added inside {} blocks or functions if the exception isn't thrown.
STL在嵌入式系统中最大的问题是内存分配问题(正如你所说,这会导致很多问题)。
我会认真研究创建自己的内存管理,通过重写 new/delete 运算符来构建。我非常确定,只要花点时间,就可以做到,而且几乎肯定是值得的。
至于例外问题,我不会去那里。异常会严重减慢代码速度,因为它们会导致每个块 (
{ }
) 前后都有代码,从而允许捕获异常并销毁其中包含的任何物体。我手头上没有这方面的硬数据,但每次我看到这个问题出现时,我都会看到压倒性的证据表明使用异常会导致速度大幅下降。编辑:
由于很多人写评论指出异常处理不慢,我想我应该添加这个小注释(感谢在评论中写下此内容的人,我认为最好在此添加)。
异常处理减慢代码速度的原因是编译器必须确保从引发异常的位置到处理异常的位置的每个块 (
{}
) 都必须释放其中的所有对象它。这是添加到每个块的代码,无论是否有人抛出异常(因为编译器无法在编译时判断该块是否将成为异常“链”的一部分)。当然,这可能是一种旧的处理方式,在较新的编译器中速度更快(我不太了解 C++ 编译器优化的最新情况)。最好的了解方法就是运行一些示例代码,打开和关闭异常(其中包括一些嵌套函数),并对差异进行计时。
The biggest problem with STL in embedded systems is the memory allocation issue (which, as you said, causes a lot of problems).
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 slowdown of 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 not slower, 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.
在我们的嵌入式扫描仪项目中,我们正在开发一个带有 ARM7 CPU 的板,并且 STL 没有带来任何问题。当然,项目细节很重要,因为动态内存分配对于当今的许多可用板和项目类型来说可能不是问题。
On our embedded scanner project we were developing a board with ARM7 CPU and STL didn't bring any issue. Surely the project details are important since dynamic memory allocation may not be an issue for many available boards today and type of projects.
了解在嵌入式环境中 C++ 相对于 C 的一些优势。使用 C++ 并不总是需要异常处理或 RTTI 或动态内存管理。您可以使用编译器选项来关闭它们。并非所有 STL 容器都使用动态内存,并且除了 iostream 之外,很少有容器使用 RTTI。大多数函数都标记为
noexcept
;这意味着他们不希望有例外。C++ 相对于 C 的主要优势在于,前者将一切都推向编译时,而后者则试图在运行时实现多态性。编译时工作负载意味着及早检测故障,防止生产灾难。 C缺乏编译时计算所需的抽象机制;它也没有提供足够的设施来确保类型安全和保障。 C 没有在所有代码库中完全被 C++ 取代的唯一原因是心理惰性(包括 Linus Torvalds 的 C++ 恐惧症)。如果 C++ 社区没有为 C 库提供 C++ 绑定,而是尝试相反的做法,那么好处现在就会显而易见。 C++ 社区应该提供重要库的第一手重写,以及用于向后兼容的 C 绑定,以展示 C++ 真正的强大潜力。
Take a look at some benefits of C++ over C for embedded environments. You do not always need exception handling or RTTI or dynamic memroy management for using C++. You have the compiler options to turn them off. Not all STL containers us dynamicemory and very few - other than iostream - use RTTI. Most of the functions are marked
noexcept
; meaning they don't expect exceptions.The major avantage of C++ over C is that the former is pushing everything toward compile-time, while the later is trying to achieve polymorphism at run-time. Compile-time workload means early failure detection preventing production catastrophy. C lacks the abstraction mechanisms necessary for compile-time computation; Nor does it provide enough facilities for ensuring type safety and security. The only reason for C not being totally replace with C++ in all codebases is mental inertia (includes Linus Torvalds' C++phobia). If - instead of providing C++ bindings for C libraries - C++ communities had tried the reverse, the benefits would've been vivid by now. C++ community should provide 1st hand rewrite of important libraries, along with C bindings for backwards compatibility to showcase the real beastfull potentials of C++.