OpenEdv-开源电子网

 找回密码
 立即注册
正点原子全套STM32/Linux/FPGA开发资料,上千讲STM32视频教程免费下载...
查看: 2583|回复: 1

为什么C ++在嵌入式系统设计中是C的可行替代品

[复制链接]

31

主题

31

帖子

0

精华

初级会员

Rank: 2

积分
147
金钱
147
注册时间
2020-5-13
在线时间
16 小时
发表于 2020-5-28 16:03:10 | 显示全部楼层 |阅读模式

你是一名嵌入式开发工程师。你知道C是适合该工作的语言,尽管有时维护周期可以重复很长时间。有时你会感到that琐,就像在编码自动机一样,反复在结构上创建基本迭代,这些结构与上周或上个月的结构非常相似。

你已经听说过C ++作为一种功能强大的语言的销售趋势,但是你也听说过有关C ++巨大足迹的恐怖故事,这使C ++成为嵌入式应用的不二之选。此外,它是如此复杂:必须确实很难使用。

这有一个熟悉的戒指吗?嵌入式开发市场涵盖了广泛的应用领域,包括汽车,医疗,国防和电信。尽管C作为嵌入式开发的强大语言无疑享有良好的声誉,但C ++却没有那么广泛的吸引力。在当今功能强大且复杂的C ++环境中,这通常是不正确的信念。

为什么C ++可以替代C?作为一种语言,它当然是从C语言的根源发展而来的。仅使用aC ++编译器重新编译C项目将产生更严格的代码类型检查(可能需要先克服一些声明上的不兼容性)。

一旦掌握了其核心功能,C ++就可以提供更大的数据抽象,这对于更大,更复杂的软件系统来说是一个重要的目标。面向对象(OO)使这一抽象更进一步,你可以用类功能替换全局“工作者”功能。

模板可能是抵制C ++使用的最大恐惧因素,并且经常被引用为大型代码膨胀经验的原因或推测。但是,实际上,精心设计的模板代码提供了一种优雅的方式来一致地处理各种数据类型。C ++的StandardLibrary是此类通用编程风格的广告。

异常处理是另一个遭受FUD(恐惧,不确定性和怀疑)困扰的领域。许多C软件系统中的异常通常是手动的编程任务。可以用更优雅的基于C ++异常的解决方案替换此异常处理机制,尽管需要付费。

与往常一样,只有在对开发团队进行需求更改时,这种优雅的解决方案才能得到应有的认可。精心设计的基于对象的设计在这种情况下可以适应的速度部分归因于更好的实现抽象和隐藏。

编译器性能

坦率地说,比较C和C ++编译器实现可能令人讨厌。从这种苹果与橘子的比较中很难获得科学的结果。

有趣的新闻组讨论报告了C ++实现的空间和性能效率损失在8%到30%之间。当然,对于使用相同Clibrary的完全相同的代码,我们希望使用相同的二进制代码。

在其产生的过程中,毫无疑问在速度方面,特别是在可执行和运行时空间需求方面,C ++性能的经验很差。在C ++的采用初期,这些担忧导致了有趣的行业形成。

十年前,主要是日本嵌入式系统提供商的合作提出了一种严格受限的C ++语言语言环境,以满足嵌入式需求。该概念禁止使用C ++语言,包括异常处理,运行时类型信息,命名空间,模板,多重继承和虚拟函数。

其理由有两个主要方面。首先是消除导致可怕的“代码膨胀”的语言构造。第二个目的是从学习的角度简化C ++语言,也许是因为嵌入式工程师的OO经验水平较低。根本有缺陷

但是,在充分尊重作者的前提下,EmbeddedC ++的方法似乎存在根本缺陷。C ++语言委员会的勤奋工作的成员一定听说过放弃其精心设计的标准C ++语言的全部内容的提议,对此感到灰白。

他们对该建议的集体回应是开始审查该语言及其最受欢迎的实现(编译器和机器环境)中的实际性能问题。

结果是C ++标准委员会发布的一份全面报告,内容涉及完整C ++语言每个主要功能的性能预期。报告中有一个小节介绍了每个功能的性能和空间效率(静态和运行时)。本文总结了最有趣的功能的发现。

a)命名空间:与使用命名空间没有空间或时间开销。它们仅在编译时影响名称查找规则。命名空间的主要优点是提供了一种在大型项目中对名称进行分区的机制,以避免名称冲突。顺便说一句,using指令通过将所有未限定的标识符移动到当前名称空间中,避免了在使用显式名称空间限定时进行额外的键入工作。

b)TypeConversions: C ++带有C样式转换符号,但是通过四个适用于不同转换情况的newoperator支持更安全和显式的转换。对于这些新样式转换操作符中的三个(const_cast,static_cast和reinterpret_cast),不涉及性能。

实际上,编译器在生成目标代码时通常会将转换符号转换为这些新的类型转换运算符之一。如果需要的转换需要运行时类型信息(RTTI)机制(例如,在类层次结构中进行交叉转换),则只有dynamic_cast可能会涉及额外的开销,我们将在后面介绍。

c)ClassRepresentation: 与C的structand全局函数等效项相比,基本的类功能并不感到任何空间或速度开销,这有些令人惊讶。这是因为非虚拟函数和静态数据成员与类定义一起存储,而不是存储在类的对象中。

成员函数调用还有一个额外的隐式指针参数,必须指向类对象(* this)。另一方面,独立的函数调用通常需要通过等效的显式指针参数将操作数据明确地传递给它们。

d)VirtualFunctions: 虚拟函数会产生明确定义的成本,该成本基于基础操作:索引到函数指针数组中。这是一种在Ccode中很常见的实现技术,但在虚拟函数范式中表达得更为优雅。

在某些情况下,使用虚函数可能会导致“代码膨胀”。如果包含虚拟函数的类模板针对各种类型进行了专门化,则每个专门化都将保留重复的成员函数及其相关的支持结构,包括虚拟表。

通常,这将导致过多的目标代码,因为当前的链接器/优化器技术不够成熟,无法识别这种情况。为避免此问题,你可以将通用代码(不依赖于实例化类型)移出类模板并移入非模板帮助器函数,或者将功能性从模板类移至非模板基类。

e)FunctionInlining: 就提高性能效率而言,应避免使用函数调用。C ++的内联功能为编译器提供了可以内联到其调用位置的函数的提示。编译器没有义务接受此提示。先进的优化技术可以自动识别和删除较小且不太复杂的函数调用,而无需代码明确提供内联提示。但是,迄今为止的经验表明,隐式内联不会产生一致的好处,因此应该使用显式内联关键字。

f)虚拟基类(VBC):在非虚拟继承中,成员函数调用执行简单的常量调整。与VBC的本质区别在于成员函数需要在运行时执行查找,以发现应激活继承树中的哪个类函数。与非虚拟案例相比,这涉及大约15%的额外开销。

但是,通过另一个功能来模拟虚拟呼叫的功能也会带来成本。实现在组成类周围传递的接口类的另一种技术本身确实需要在访问中使用间接方式,从而导致随之而来的成本和开销。

g)运行时类型信息:

RTTI用于询问对象的类型,它也是dynamic_cast功能基础结构的一部分。为了说明其性能,请考虑使用dynamic_cast的应用程序:查找对象的vtable,找到该对象的派生对象最多的对象,使用该对象的type_info对this指针执行所需的调整。

h)异常处理: C ++的异常处理功能在运行时需要类型信息,并且与RTTI结构部分重叠并扩展了RTTI结构。但是,任何手动编码备选方案都必须考虑编码风格,错误处理例程的完整覆盖,线程安全性,运行时系统开销以及处理错误的开销。考虑相关的开销,运行时开销和代码维护开销可以进行异常处理合理的选择。

i)模板:就空间成本而言,模板是C ++最受人谴责的功能之一。当类和函数模板为具有不同参数的每个实例生成一组新的代码和数据时,就会出现代码膨胀。

对相同专业化的多个实例化与许多不同专业化进行的测试表明,性能结果差异很大,这表明编译器有一些方法可以满足该领域的优化目标。实施优化

通过启用某些功能(尤其是部分专业化功能),编译器可以允许图书馆供应商实施优化以克服此问题。开发人员可以通过通用类模板路由所有实例化请求,例如通过对基于void *的单个专业化使用通用类模板,来部署避免多个专业化的技术。文章来源:http://emb.hqyj.com/Column/20209490.html


正点原子逻辑分析仪DL16劲爆上市
回复

使用道具 举报

0

主题

67

帖子

0

精华

初级会员

Rank: 2

积分
71
金钱
71
注册时间
2018-12-27
在线时间
0 小时
发表于 2020-5-28 16:03:25 | 显示全部楼层
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则



关闭

原子哥极力推荐上一条 /2 下一条

正点原子公众号

QQ|手机版|OpenEdv-开源电子网 ( 粤ICP备12000418号-1 )

GMT+8, 2025-6-11 04:14

Powered by OpenEdv-开源电子网

© 2001-2030 OpenEdv-开源电子网

快速回复 返回顶部 返回列表