没有虚函数的命令模式(C ++)

时间:2022-11-25 16:54:39

For performance reasons, I am using the the Curiously Reoccuring Template Pattern to avoid virtual functions. I have lots of small commands which execute millions of times. I am trying to fit this into the Command Pattern. I want to add tons of commands to a queue, and then iterate through them executing each one by one. Each Command uses a CRTP to avoid virtual functions. The problem I am running into is that the Command pattern is typically implemented using a vector of pointers. But when the Command class is templated, it becomes hard to pass around generic Command pointers. I'm not a C++ expert, so perhaps there is an obvious way to store a vector of templated command objects? I have been trying to use something like:

出于性能原因,我使用Curiously Reoccuring模板模式来避免虚函数。我有很多执行数百万次的小命令。我试图将其纳入命令模式。我想在队列中添加大量命令,然后迭代执行每个命令。每个命令都使用CRTP来避免虚函数。我遇到的问题是Command模式通常使用指针向量来实现。但是当Command类被模板化时,很难传递通用的Command指针。我不是C ++专家,所以也许有一种显而易见的方法来存储模板化命令对象的向量?我一直试图使用类似的东西:

boost:ptr_vector commands;
AddCommand(Command* command) {
  commands.push_back(command);
}

The problem is Command is not a type, so Command* command gives a compile error. I need to use Command<CommandType>, but that won't work because I need the queue to hold different types of commands.

问题是Command不是类型,因此Command *命令会产生编译错误。我需要使用Command ,但这不起作用,因为我需要队列来容纳不同类型的命令。

Any ideas for solutions? Or are virtual functions my only option?

任何解决方案的想法?或者虚拟功能是我唯一的选择吗?

ADDED: The command objects are part of a monte carlo simulation algorithm. So you might have, Command be a random number from a normal distribution, where the parameters of the normal distribution are part of the class. So the command pattern fits very nicely. I have lots of calls, in a particular order, to functions that need to maintain state.

ADDED:命令对象是蒙特卡罗模拟算法的一部分。所以你可能有,Command是来自正态分布的随机数,其中正态分布的参数是类的一部分。因此命令模式非常适合。我按照特定的顺序对需要维护状态的函数进行了大量调用。

3 个解决方案

#1


The CRTP does its magic by resolving the run time type of the object at compile time so that the compiler can inline the function calls. If you have a vector of pointers to a generic type, the compiler cannot determine the specific concrete type, and will not be able to do its compile time resolution.

CRTP通过在编译时解析对象的运行时类型来实现其魔力,以便编译器可以内联函数调用。如果您有一个指向泛型类型的指针向量,则编译器无法确定具体的具体类型,并且无法执行其编译时解析。

From just the information you have in your question, I think virtual functions are your best option. However, virtual functions are not that slow. They are slower than an in-lined function, sure, but in many cases they are plenty fast enough! Especially if your process is bounded by I/O time instead of processing time.

从您在问题中的信息,我认为虚拟功能是您的最佳选择。但是,虚函数并不那么慢。它们比内联功能慢,当然,但在很多情况下它们足够快!特别是如果您的过程受I / O时间限制而不是处理时间。

One of the answers to this question has some more in depth discussion of this issue. To summarize, the overhead for a virtual function call will likely be measured in nanoseconds. It is more complicated than that, but the point is that you shouldn't be afraid of virtual functions unless your function is doing something really trivial like a single assignment. You said that your commands were small, so perhaps this is the case. I'd try doing a quick prototype with virtual functions and see if that gives acceptable performance.

这个问题的答案之一有更深入地讨论这个问题。总而言之,虚拟函数调用的开销可能以纳秒为单位进行测量。它比这更复杂,但重点是你不应该害怕虚函数,除非你的函数做的事情像一个单独的赋值一样非常简单。你说你的命令很小,所以也许就是这种情况。我尝试用虚函数做一个快速原型,看看它是否能提供可接受的性能。

#2


Unless you are building your command queue during compile time, what you want is impossible.

除非您在编译期间构建命令队列,否则您想要的是不可能的。

#3


I can't tell if your command queue changes often or seldom.

我无法判断您的命令队列是经常更改还是很少更改。

If it changes seldom, compared to how often it is executed, it seems to me this could be a job for code generation.

如果它很少变化,与它执行的频率相比,在我看来,这可能是代码生成的工作。

Just print out a program to do the actions you need, compile & link a dll on the fly, and load it. That should take about a second. No classes, objects, or dispatching. And if you single-step it, you'll see almost no cycles that don't contribute materially to your answer.

只需打印出一个程序来执行您需要的操作,即时编译和链接dll并加载它。这应该需要大约一秒钟。没有类,对象或调度。如果你单步执行它,你几乎看不到任何对你的答案没有实质性影响的循环。

#1


The CRTP does its magic by resolving the run time type of the object at compile time so that the compiler can inline the function calls. If you have a vector of pointers to a generic type, the compiler cannot determine the specific concrete type, and will not be able to do its compile time resolution.

CRTP通过在编译时解析对象的运行时类型来实现其魔力,以便编译器可以内联函数调用。如果您有一个指向泛型类型的指针向量,则编译器无法确定具体的具体类型,并且无法执行其编译时解析。

From just the information you have in your question, I think virtual functions are your best option. However, virtual functions are not that slow. They are slower than an in-lined function, sure, but in many cases they are plenty fast enough! Especially if your process is bounded by I/O time instead of processing time.

从您在问题中的信息,我认为虚拟功能是您的最佳选择。但是,虚函数并不那么慢。它们比内联功能慢,当然,但在很多情况下它们足够快!特别是如果您的过程受I / O时间限制而不是处理时间。

One of the answers to this question has some more in depth discussion of this issue. To summarize, the overhead for a virtual function call will likely be measured in nanoseconds. It is more complicated than that, but the point is that you shouldn't be afraid of virtual functions unless your function is doing something really trivial like a single assignment. You said that your commands were small, so perhaps this is the case. I'd try doing a quick prototype with virtual functions and see if that gives acceptable performance.

这个问题的答案之一有更深入地讨论这个问题。总而言之,虚拟函数调用的开销可能以纳秒为单位进行测量。它比这更复杂,但重点是你不应该害怕虚函数,除非你的函数做的事情像一个单独的赋值一样非常简单。你说你的命令很小,所以也许就是这种情况。我尝试用虚函数做一个快速原型,看看它是否能提供可接受的性能。

#2


Unless you are building your command queue during compile time, what you want is impossible.

除非您在编译期间构建命令队列,否则您想要的是不可能的。

#3


I can't tell if your command queue changes often or seldom.

我无法判断您的命令队列是经常更改还是很少更改。

If it changes seldom, compared to how often it is executed, it seems to me this could be a job for code generation.

如果它很少变化,与它执行的频率相比,在我看来,这可能是代码生成的工作。

Just print out a program to do the actions you need, compile & link a dll on the fly, and load it. That should take about a second. No classes, objects, or dispatching. And if you single-step it, you'll see almost no cycles that don't contribute materially to your answer.

只需打印出一个程序来执行您需要的操作,即时编译和链接dll并加载它。这应该需要大约一秒钟。没有类,对象或调度。如果你单步执行它,你几乎看不到任何对你的答案没有实质性影响的循环。