C# 和 C++ 可以吗?高性能代码的互操作?
我们有传统的 C++ 代码执行高性能数据处理(例如,从硬件设备馈送的大量数据,以时间敏感的方式处理以进行显示、转换和传输到辅助存储)。
我们对 C#/.NET 的新 GUI 和新实用程序感兴趣(现有的 GUI 是 C++ MFC 和 Qt)。当然,使用现有系统,我们不存在到.NET虚拟机的“语言翻译”问题(现有代码都是C++)。
经过大量研究和阅读许多书籍,我不确定这是否可以有效地完成。可能的方法(我错过了什么吗?):
- 用.NET重写所有内容(不能 发生的情况——代码过多、裸机设备访问、时间敏感的繁重算法处理)
- 托管的薄适配器层 用于托管的C++/CLI
- 厚适配器层 C++/CLI
- 不要使用.NET(经理感觉很棒 悲伤)
我们对(2)“薄适配器层”的担忧是,如果 GUI 能够(重新)使用“业务”层中的逻辑(许多属性是通过算法导出的),那就太好了,所以如果我们不这样做暴露/包装 C++ 类,许多 GUI 逻辑将仅复制业务层中现有的 C++ 逻辑。
我们对 (3)“厚适配器层”的担忧是,用 C# 类包装每个 C++ 类似乎非常乏味(昂贵),并且有几本书建议跨越该边界的装箱/拆箱访问似乎表明这种方法相当不可行/prohibitive(除了琐碎的设计之外,它的性能令人望而却步)。
您将如何在 C++ 中实现的深度丰富的类结构之上连接新的 C#/.NET (GUI)?
We have legacy C++ code performing high-performance data processing (e.g., large volumes of data fed from hardware devices that is processed in a time-sensitive manner for display, transforms, and transfer to secondary storage).
We are interested in C#/.NET for new GUIs and new utilities (existing GUIs are C++ MFC and Qt). Of course, with the existing system we have no "language translation" issue to the .NET virtual machine (existing code is all C++).
After much study, and many books, I'm not sure this can be done effectively. Possible approaches (am I missing any?):
- Rewrite everything in .NET (can't
happen -- too much code, bare-metal device access, time-sensitive heavy algorithm processing) - Thin adapter layer for Managed
C++/CLI - Thick adapter layer for Managed
C++/CLI - Don't use .NET (managers feel great
sadness)
Our concerns about (2) "thin adapter layer" is that it would be nice if the GUIs could (re-)use the logic in the "business" layer (many properties are algorithmically derived), so if we don't expose/wrap the C++ classes, much GUI logic will merely replicate the existing C++ logic in the business layer.
Our concerns about (3) "thick adapter layer" is that it seems very tedious (expensive) to wrap each C++ class with a C# class, and several books suggest the boxing/unboxing access across that boundary appear to suggest this approach is quite unworkable/prohibitive (it's performance prohibitive beyond trivial designs).
How would you interface new C#/.NET (GUI) on top of a deep-rich-class-structure implemented in C++?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
C++/CLI 非常适合此目的。托管/非托管转换不存在性能问题,因为 C++/CLI 使用 .NET 运行时引擎本身使用的相同优化调用技术来实现字符串连接等高性能方法。
当您在同一数据结构的 .NET 和本机版本之间来回复制数据时,就会出现性能问题,但使用使用 BSTR 的库和使用 std 的库也会遇到同样的问题: :string,并且缓慢的操作同样明显(与 P/Invoke 不同,P/Invoke 试图使这些转换透明,最终隐藏了过程中的性能问题)。
您还可以使用一些技巧来克服这个问题。例如,不要将
std::vector
复制到System::Collections::Generic::List
中,而是实现一个直接调用的IEnumerator
从std::vector
读取。当然,如果数据只是直接传递回另一个 C++ 函数,则根本没有理由将其转换为托管类型。同样,C++/CLI 使保留格式变得容易,P/Invoke 会在您背后尝试转换所有内容。
总之,“薄”C++/CLI 包装层是您的最佳选择。
C++/CLI is perfect for this. There are no performance issues with the managed/unmanaged translation, since C++/CLI uses the same optimized call technique used by the .NET runtime engine itself to implement high-performance methods such as string concatenation.
The performance problems arise when you're copying data back and forth between .NET and native versions of the same data structure, but you'd have the same problem with e.g. using a library that uses BSTR alongside one that uses
std::string
, and the slow operations are equally obvious (unlike with P/Invoke, which tries to make these translations transparent, and ends up hiding the performance problems in the process).There are also some tricks you can use to overcome this. For example, instead of copying a
std::vector
into aSystem::Collections::Generic::List
, implement anIEnumerator
that directly reads from thestd::vector
.And of course, if the data is simply going to be passed directly back to another C++ function, there's no reason to convert it to a managed type at all. Again, C++/CLI makes preserving the format easy, where P/Invoke tries to convert everything behind your back.
In summary, the "thin" C++/CLI wrapper layer is the best of your options.
您对约束的想法是正确的。跨越边界的代价是昂贵的,你不想为了细粒度的操作而这样做。细粒度到什么程度?当然,这取决于。
在理想情况下,您的 C++ 代码分层为合理的对象模型,您可以在其上放置 COM 层(或类似层)以进行更细粒度的操作。举一个例子,您不想公开一个具有 5 个属性的对象,并且每个属性都有一对 setter/getter,而是希望公开一个接受所有要设置的属性的映射的 SetProperties() 方法。这绝不是您需要注意的唯一情况;这只是一个如何让自己偏向于更大粒度操作的示例。
关于 COM——它很好,但当然不是必需的。使用 COM 会强制您遵守纪律,因为您需要在 COM 接口中正式定义操作。如果没有 COM 提供的正式执行,您的团队可能会“作弊”,并暴露各层之间的大量战术集成点,这可能会导致潜在的性能问题。
如果您有扎实的项目管理和优秀的团队成员,那么您可以执行这些项目标准,而无需依赖 COM 的形式。在 C# 中定义您自己的包装类,所有边界交叉都通过该类发生。
我怀疑,最重要的是,除非您尝试并测试它,否则您不会确切地知道正确的决定。
找几个团队成员,优秀的开发人员,让他们构建两种不同选项的原型。例如,薄与厚,这些术语的确切含义由您定义。给他们 3 周左右的时间来整理一些东西。然后衡量他们的生产力和绩效,并据此做出决定。
You have the right idea about the constraints. Crossing the boundary is expensive, you don't want to do it for fine-grained operations. How fine-grained? That depends, of course.
In the ideal case your C++ code is layered into a rational object model, over which you can put a COM layer (or similar) for larger-grained operations. As one example, rather than exposing an object with 5 properties, and a setter/getter pair on each one, you'd want to expose a SetProperties() method that accepts a map of all the properties to be set. This is by no means the only case you need to look out for; it's just an example of how to bias yourself toward larger-grained operations.
About COM - it's nice but not required of course. Using COM enforces a discipline on you, in that you need to formally define the operations in the COM interface. Without the formal enforcement provided by COM, your team could possibly "cheat" - and expose numerous tactical integration points between the layers, which can result in a sneaky performance problem.
If you have solid project management and good team members, then you could do the enforcement of these project standards without relying on the formality of COM. Define your own wrapper classes in C#, through which all boundary-crossing occurs.
The bottom line, I suspect, is that you won't know exactly the right decision, until you play around with it and test it.
Get a couple team members, good devs, and have them build prototypes of two different options. Eg, thin vs thick, where the exact meanings of those terms are defined by you. Give them 3 weeks or so to put something together. Then measure their productivity and performance, and make a decision based on that.