我正在尝试在应用程序架构中非常低的级别对我的代码进行微优化。所以这是我的具体场景:
- 我有一个解析器类,它解析图形文件(节点、边、邻接条目等)
- 文件格式是版本化的,因此每个版本都存在解析器,它们被实现为单独的类(ParserV1、ParserV2、...)。
- 解析器为应用程序中的某些上层提供相同的功能。因此,它们实现了相同的“接口”。
- 在 C++ 中,我会将这样的接口实现为一个抽象类,其中所有函数都是纯虚拟的。
- 由于虚函数需要另一个内存查找,并且不能在编译时静态绑定,而且——更重要的是——不允许在解析器类中内联小方法,使用经典的子类化习惯不会导致我能达到的最佳表现。
[在描述我可能的解决方案之前,我想解释一下为什么我在这里做微优化(你可以跳过这一段):解析器类有很多小方法,其中“小”意味着他们没有做太多. 它们中的大多数只从缓存的比特流中读取一两个字节,甚至只读取一个比特。所以应该有可能以一种非常有效的方式来实现它们,其中一个函数调用,当内联时,只需要少量的机器命令。这些方法在应用程序中被非常频繁地调用,因为它们在一个非常大的图表(全球道路网络)中查找节点属性,每个用户请求可能发生大约一百万次,并且这样的请求应该与可能的。]
去这里的路是什么?我可以看到以下解决问题的方法:
- 使用纯虚方法编写接口并将其子类化。性能会受到影响。
- 不要写这样的接口。每个解析器自己定义相同的方法。在上层(使用解析器)有指向每个版本子类的指针(作为成员)。一开始,实例化应该使用的特定解析器。每当访问函数时,使用 switch 块并将解析器实例强制转换为显式子类。性能会更好吗?(if/switch 块与虚拟表查找)。
- 混合两种解决方案 1. + 2.:为很少使用的方法编写一个带有纯虚拟方法的接口,其中性能不是很关键。如果很关键,不要提供虚拟方法,而是使用第二种方法。
- 改进2.:在抽象类中提供非虚方法;在抽象类中保留版本号作为成员变量(一种自己的运行时类型信息),并在这些方法中实现 if/switch 块和强制转换;然后调用子类中的方法。这提供了内联和静态绑定。
有没有更好的方法来解决这个问题?这有什么成语吗?
澄清一下,我有很多与版本无关的函数(至少到现在为止),因此非常适合某些超类。我将对大多数函数使用标准的子类化设计,而这个问题仅涵盖要优化的版本相关函数的解决方案。(其中一些调用不频繁,在这些情况下我当然可以使用虚拟方法。)除此之外,我不喜欢让解析器类决定哪些方法需要高性能,哪些方法不需要. (尽管这样做是可能的。)