我正在编写一个具有多种存储类型('stypes')和多种数据类型('dtypes')的矩阵库(SciRuby 的一部分)。例如,矩阵的stype
目前可能是密集、耶鲁(又名“csr”)或列表列表;及其 dtype
可能是int8
, int16
, int32
, int64
, float32
, float64
, complex64
等
用 Ruby 或 sed 编写模板处理器非常容易,它采用基本函数(如稀疏矩阵乘法)并为每个可能的 dtype
创建一个自定义版本。 。我什至可以编写这样一个模板来处理两种不同的数据类型,比如说,如果我们想乘以 int32
通过float64
.
在某些情况下,对于不同的类型可以执行相同的操作。但最终,您可能会得到一大堆函数,其中许多函数在大多数人的使用过程中甚至从未被使用过。
使用函数指针数组来访问这些函数也很容易——想象一下,即使是 3 维函数指针数组也不是太难:
MultFuncs[lhs->stype][lhs->dtype][rhs->dtype](lhs->shape[0], rhs->shape[1], lhs->data, rhs->data, result->data);
// This might point to some function like this:
// i32_f64_dense_mult(size_t, size_t, int32_t*, float64*, float64*);
当然,函数指针数组的极端替代方案是分层的 switch
,它的编码和维护会非常复杂。或if
/else
声明:
switch(lhs->stype) {
case STYPE_SPARSE:
switch(lhs->dtype) {
case DTYPE_INT32:
switch(rhs->dtype) {
case DTYPE_FLOAT64:
i32_f64_mult(lhs->shape[0], rhs->shape[1], lhs->ija, rhs->ija, lhs->a, rhs->a, result->data);
break;
// ... and so on ...
这似乎也是 O(sd2),其中 s= stypes 数量,d=每个操作的数据类型数,而函数指针数组将为 O(r),其中 r= 数组中的维数。
但还有第三种选择。
第三种选择是使用函数指针数组进行常见操作(例如,从一种未知类型复制到另一种未知类型):
SetFuncs[lhs->dtype][rhs->dtype](5, // copy five consecutive items
&to, // destination
dtype_sizeof[lhs->dtype], // dtype_sizeof is a const size_t array giving sizeof(int8_t), sizeof(int16_t), etc.
&from, // source
dtype_sizeof[rhs->dtype]);
然后从通用稀疏矩阵乘法函数中调用它,可以这样声明:
void generic_sparse_multiply(size_t* ija, size_t* ijb, void* a, void* b, int dtype_a, int dtype_b);
这将使用 SetFuncs[dtype_a][dtype_b]
例如,引用正确的赋值函数。那么,缺点是您可能必须实现一大堆 - IncrementFuncs、DecrementFuncs、MultFuncs、AddFuncs、SubFuncs 等 - 因为您永远不知道会出现什么类型。
最后,我的问题是:
- 拥有巨大的多维函数指针常量数组的成本是多少(如果有的话)?大型库或可执行文件?加载时间慢?等等
- 是否使用
IncrementFuncs
等泛型,SetFuncs
等(可能都依赖于memcpy
或类型转换)对编译时优化存在障碍? - 如果要使用如上所述的 switch 语句,现代编译器会优化这些语句吗?还是每次都会对其进行评估?
我意识到这是一系列极其复杂的问题。
如果您可以简单地向我推荐一个好的资源并且不想直接回答,那完全没问题。在发布此内容之前,我广泛使用了 Google,但不太确定要使用哪些搜索词。
最佳答案
首先,尝试降低函数的复杂性。您应该能够有一个类似的声明
Result_t function (Param_t*);
其中 Param_t 是一个包含您传递的所有内容的结构。要使用泛型类型,请在结构中包含一个枚举,告知使用哪种类型,并在该类型中包含一个 void*。
1.What is the cost, if any, of having enormous multi-dimensional const arrays of function pointers? Large library or executable? Slow load time? etc.
绝对更大的可执行文件。加载时间取决于代码适用的系统。如果是基于 RAM 的系统(PC 等),则加载时间可能会增加,但不会对性能产生任何重大影响。当然,这取决于“巨大”有多大:)
2.Does use of generics like IncrementFuncs, SetFuncs, etc. (which all probably depend on memcpy or typecasts) present barriers to compile-time optimization?
可能不会,编译器可以优化的东西太多了。在处理 C 中的通用数据类型时,它通常最终归结为 memcpy(),它本身有望实现与复制一样快。
3.If one were to use switch statements as described above, would these be optimized out by modern compilers? Or would they be evaluated every single time?
讽刺的是,编译器可能会将其优化为函数指针数组之类的东西。然而,编译器可能无法预测数据的性质,特别是如果它是在运行时设置的。
关于c - C 中泛型函数与函数指针数组相比有何缺点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9289272/