
介绍
DOP(面向数据编程)是一种编程策略,当组织试图优先考虑性能和可维护性时,它非常适用。与 OOP(面向对象编程)和 FP(函数式编程)相比,DOP 在过去五年中取得了显著进步。OOP 更关注对象及其行为,而 FP 专注于不可变性和函数。DOP 完全专注于数据如何在内存中存储以及如何检索,以提高速度和内存使用。通过降低内存访问延迟,增加 CPU 缓存占用,并允许并行处理,它寻求更快地编程。
DOP 对数据和显式转换的强调导致了更可预测和可控的程序状态,这是一个特别且值得注意的优点,尤其是在复杂的、数据密集型的应用程序中。近年来,DOP 因其高性能计算而广受欢迎,特别是在与高频交易(HFT)公司、游戏开发和数据密集型应用相关的领域。
面向数据编程的一些基础知识
DOP 将数据与行为分离,并致力于数据存储和检索,而 OOP 将数据和行为捆绑在对象中。DOP 没有 FP 那么严格,它鼓励不可变性以增强可预测性和并行处理,并将数据存储在连续的内存块中,以实现更快的数据访问和改进的 CPU 缓存局部性。
此外,DOP 通过直接将数据插入内存来访问数据,从而减少了指针和引用的使用,以消除缓存未命中。最后,DOP 使用单指令多数据(SIMD)算法批量处理数据,以操作大数据集,而不是逐个处理单个条目。
各种技术,如 SIMD,帮助 DOP 同时执行多个数据。另一种技术是并行处理,它使用多核来提高速度,并进行内存对齐,以防止分散数据导致的减速。
诸如实体组件系统(ECS)之类的系统有助于管理大量数据,而诸如数据管道之类的策略则保持应用程序中的数据流。DOP 被游戏和高频交易公司所使用,在这些公司中,数据性能是主要的关注点。
面向数据编程的优势
在处理大型数据集时,DOP 具有优势,因为它将数据存储在连续的内存位置,从而提高了数据访问速度。数据和行为分别存储,因此逻辑或数据结构的任何变化都不会产生太大影响,从而表现出灵活性和可维护性。SIMD 和多核处理器等策略利用了 DOP,因为它针对现代硬件进行了优化;其结构存储数据集,以便数据增长不会造成问题(例如,数据碎片化)。
通过利用并行计算平台的性能,从而优化数据布局,DOP 可以用于高性能计算。DOP 中数据和行为的分离(与 OOP 中数据和行为的紧密耦合相反)减少了程序不同部分之间的耦合,允许用户在不影响整个系统的情况下修改或替换组件。
面向数据编程的劣势
对于 OOP 开发人员来说,仔细考虑 DOP 所需的数据结构和内存布局规划似乎很困难。对于较小的项目或软件,其中性能不是优先考虑的,DOP 方法似乎是一个不太可行的观点,这可能会增加项目的复杂性。DOP 对性能的主要关注可能导致过度优化;任何代码修改都需要对代码有更深入的理解。
将 DOP 与面向对象或过程化范式集成可能很复杂,需要仔细设计以管理性能、可维护性和代码清晰度之间的权衡。DOP 强调保持数据和行为的分离。如果数据转换没有得到妥善的管理,可能会出现代码重复。与 OOP 相比,专门为 DOP 设计的库、框架和工具较少。
面向对象编程、函数式编程、面向数据编程之间的比较
用代码来理解
面向对象编程
在面向对象编程(OOP)中, area()
方法是一种行为,被封装在 Rectangle
类中。这可以看作是一个缺陷,因为它会由于对象的创建和每个对象调用的方法类而造成开销。这种方法的唯一优点是它创造了模块化和可重用性。然而,当存在大量数据集时,这可能是一个缺点。

面向数据编程
在面向数据编程(DOP)中,宽和高字段被分离成单独的数组,以获得更好的内存局部性。这被证明是有利的,因为它增强了有效的内存利用和更好的性能,而不会产生任何开销,特别是在处理大量数据集的情况下。这导致代码更有效和性能更高,特别是在处理大量数据时。

函数式编程
在函数式编程(FP)中,主要目标是提高架构的清晰度,因为 FP 逻辑被封装在高阶函数中。它们通过纯虚拟函数和流管道被最小化。在这里,使用 map()
和 forEach()
方法以最小化副作用的方式封装逻辑,展示了可组合性和函数的纯度。虽然 DOP 示例更侧重于硬件意识优化,但面向函数的编程更倾向于代码可读性、可测试性和可维护性。

面向数据编程的趋势特征
数组结构(SoA)对 DOP 很有用,它促进了 SoA 布局,使数据局域性和内存吞吐量最大化,从而使 SoA 模式更接近像 Java 这样的语言。旧的 OOP 语言不支持 SoA。
ECS 框架对 DOP 很有用,最近在游戏开发组织中通过 ECS 架构采用了 DOP。此外,基于 Java 的 ECS 引擎使得以具有高吞吐量的内存友好格式来处理实体数据成为可能。
通过数据切片实现的并行性 DOP 系统将大型数据集分解为并行处理的连续内存块。这是使用单一线程处理每个基于任务的模型的更好替代方案。这些数据片用于批处理引擎,其中吞吐量是主要的关注点。
SIMD 感知数据布局对 DOP 框架很有用。由于现代 CPU 支持 SIMD 操作,DOP 框架正在进行优化,以增强 SIMD 执行的内存结构。像Vector API这样的库,目前处于第九个孵化阶段,可以使 DOP 在 JVM 上更具性能导向。
面向批处理利用 DOP 资源。在 DOP 中,数据以大容量而不是实时的小批量处理。还可以按月、每周或每天安排处理,特别是在非高峰时段,这有利于优化系统资源的利用率。这种调度在游戏引擎和视频处理中是高效的。
未命名模式和变量在 Java 21 中作为预览特性引入,并在 Java 22 中最终确定。这个Java特性允许开发人员编写更符合 DOP 原则的代码。DOP 的主要特性是代码应该易于阅读和理解,特别是在处理数据转换时。使用结构化数据,如记录或复杂数据对象,是 DOP 的常见部分。未命名模式和变量特性提供了一种快速有效的方法来分解复杂的结构,只保留所需的信息。通过明确识别哪些数据是相关的,哪些不是,未命名模式和变量增强了可读性。
由于 Java 21 的特性,程序员可以编写更紧密地遵循 DOP 原则的代码。在 Java 21 中,数据清晰度、不可变性和高效的数据操作被优先考虑,这有助于开发更可靠、更可维护和更高效的程序。
结论
OOP 在结构和行为紧密耦合的地方是有益的。DOP 在性能关键型应用(如游戏和 HFTs)中是有利的;FP 可以用来保持清晰的代码以获得更好的可读性。然而,DOP 有其自身的挑战,因为与 OOP 和 FP 相比,它缺乏更大的社区支持。此外,它可能不是每个项目的最佳选择,但了解 DOP 在哪些方面可以优于 OOP 和 FP 至关重要。有很多关于面向数据编程的文章,但其中一些文章并未解释使用 DOP 的实际解决方案。
有些文章没有介绍 DOP 背后的基本思想,这对于初学者来说可能很难理解,而有些文章没有解决实际的挑战或限制。DOP 和 OOP 都支持可扩展性,但是方法不同,因为 OOP 支持使用继承和封装来创建可以随着时间演变以满足新要求的适应性软件,而 DOP 可以通过优化数据访问模式和减少与对象管理相关的开销来增强可扩展性。更准确地说,DOP 是为处理大型数据集而优化的,而 OOP 是为处理单个对象及其内部状态而优化的。
通过仔细应用 DOP 技术,开发人员可以制作出可扩展且高效的软件。这种技术的应用为需要快速计算和高效数据处理的领域创造了新的机会。
原文链接:
评论