我目前正在优化数值分析代码。在代码中,有一个在每次运行开始时构造的 200x150 元素查找表(当前是静态 std::vector >
)。查找表的构造实际上相当复杂——查找表中的值是使用迭代割线法对一组复杂的方程进行构造的。目前,对于模拟来说,查找表的构建需要运行时间的 20%(运行时间约为 25 秒,查找表构建需要 5 秒)。虽然 5 秒看起来似乎不多,但在运行 MC 模拟时,我们运行超过 50k 次模拟,它突然变成了很大的时间块。
除了其他一些想法之外,还有一件事已经浮出水面——我们可以在编译时使用模板构建这个查找表吗?表本身永远不会改变。对大型数组进行硬编码并不是一个可维护的解决方案(生成表的方程不断被调整),但似乎如果可以在编译时生成表,它将为我们提供两全其美的解决方案(易于维护,运行时无开销)。
因此,我提出以下(非常简化的)方案。假设您想在编译时生成一个静态数组(使用最适合您的任何容器 - 2D c 数组、向量向量等)。您定义了一个函数,
double f(int row, int col);
其中返回值是表中的条目,row 是查找表行,col 是查找表列。是否可以使用模板在编译时生成此静态数组,以及如何生成?
I am currently in the process of optimizing a numerical analysis code. Within the code, there is a 200x150 element lookup table (currently a static std::vector <std::vector<double>>
) that is constructed at the beginning of every run. The construction of the lookup table is actually quite complex- the values in the lookup table are constructed using an iterative secant method on a complicated set of equations. Currently, for a simulation, the construction of the lookup table is 20% of the run time (run times are on the order of 25 second, lookup table construction takes 5 seconds). While 5-seconds might not seem to be a lot, when running our MC simulations, where we are running 50k+ simulations, it suddenly becomes a big chunk of time.
Along with some other ideas, one thing that has been floated- can we construct this lookup table using templates at compile time? The table itself never changes. Hard-coding a large array isn't a maintainable solution (the equations that go into generating the table are constantly being tweaked), but it seems that if the table can be generated at compile time, it would give us the best of both worlds (easily maintainable, no overhead during runtime).
So, I propose the following (much simplified) scenario. Lets say you wanted to generate a static array (use whatever container suits you best- 2D c array, vector of vectors, etc..) at compile time. You have a function defined-
double f(int row, int col);
where the return value is the entry in the table, row is the lookup table row, and col is the lookup table column. Is it possible to generate this static array at compile time using templates, and how?
发布评论
评论(5)
通常最好的解决方案是代码生成。在那里您拥有所有的自由,并且可以确定输出实际上是一个
double[][]
。Usually the best solution is code generation. There you have all the freedom and you can be sure that the output is actually a
double[][]
.程序第一次运行时将表保存在磁盘上,只有在丢失时才重新生成它,否则从缓存中加载它。
在文件中包含版本字符串,以便在代码更改时重新生成。
Save the table on disk the first time the program is run, and only regenerate it if it is missing, otherwise load it from the cache.
Include a version string in the file so it is regenerated when the code changes.
这里有几件事。
你想做的事几乎肯定至少部分可能。
浮点值是无效的模板参数(只是,不要问为什么)。尽管您可以使用 N1/N2 表示法在模板中表示有理数,但您可以对它们执行的数学运算量并不包含可以对有理数执行的整个集合。例如 root(n) 不可用(请参阅 root(2))。除非您想要静态双变量的大量实例化,否则您将希望您的
value
访问器是一个函数。 (也许您可以想出一个新的模板浮点表示形式,将 exp 和 mant 分开,然后您就可以像使用 double 类型一样好......玩得开心:P)元编程代码很难格式化一种清晰的方式。此外,就其本质而言,它很难阅读。即使专家也很难分析一段他们没有编写的 TMP 代码,即使它相当简单。
如果实习生或任何高级级别以下的人想到只看 TMP 代码,他们的头就会爆炸。尽管如此,有时高级开发人员会因为对新事物感到恐惧而变得更大声(让你的老板感到无能可能会产生严重的影响,尽管这不应该)。
所有这些都表明......模板是一种图灵完备的语言。您可以用它们做“任何事情”......我们所说的任何事情是指不需要某种外部能力(例如系统访问)的任何事情(例如,因为您不能让编译器产生新线程)。你可以建造你的桌子。然后您需要回答的问题是您是否真的愿意。
A couple of things here.
What you want to do is almost certainly at least partially possible.
Floating point values are invalid template arguments (just is, don't ask why). Although you can represent rational numbers in templates using N1/N2 representation, the amount of math that you can do on them does not encompass the entire set that can be done on rational numbers. root(n) for instance is unavailable (see root(2)). Unless you want a bajillion instantiations of static double variables you'll want your
value
accessor to be a function. (maybe you can come up with a new template floating point representation that splits exp and mant though and then you're as well off as with the double type...have fun :P)Metaprogramming code is hard to format in a legible way. Furthermore, by its very nature, it's rather tough to read. Even an expert is going to have a tough time analyzing a piece of TMP code they didn't write even when it's rather simple.
If an intern or anyone under senior level even THINKS about just looking at TMP code their head explodes. Although, sometimes senior devs blow up louder because they're freaking out at new stuff (making your boss feel incompetent can have serious repercussions even though it shouldn't).
All of that said...templates are a Turing-complete language. You can do "anything" with them...and by anything we mean anything that doesn't require some sort of external ability like system access (because you just can't make the compiler spawn new threads for example). You can build your table. The question you'll need to then answer is whether you actually want to.
为什么不设立单独的程序呢?一种生成表并将其存储在文件中,另一种加载文件并对其运行模拟。这样,当您需要调整生成表格的方程时,您只需要重新编译该程序。
Why not have separate programs? One that generates the table and stores it in a file, and one that loads the file and runs the simulation on it. That way, when you need to tweak the equations that generate the table, you only need to recompile that program.
如果你的表是一堆整数,那么是的,你可以。或许。但你肯定不能做的是在编译时生成双精度数。
更重要的是,我认为简单的 double[][] 会比这里的向量向量更好 - 您正在为静态大小的表推送大量动态分配。
If your table was a bunch of ints, then yes, you could. Maybe. But what you certainly couldn't do is generate doubles at compile-time.
More importanly, I think that a plain double[][] would be better than a vector of vectors here- you're pushing a LOT of dynamic allocation for a statically sized table.