最便宜的参考结构?
Cheapest structure for deferencing?
假设我有一个由 unsigned int
键控的关联数组;值可以是任何固定大小的类型。有一些预定义的最大数量。实例数。
API 用法示例:MyStruct * valuePtr = get(1234);
和 put(6789, &myStructInstance);
...基本。
我希望在从该数组中快速随机读取条目时最大程度地减少缓存未命中,因此我预先malloc(sizeof(MyType) * MAX_ENTRIES)
以尽可能确保引用的位置。
泛型对于值数组很重要。我看过 C pseudo-generics,但为了简单起见更喜欢 void *
;但是,不确定这是否与性能目标不一致。最后,我想知道什么对性能最好。
我应该如何实现我的关联数组以提高性能?到目前为止的想法...
- 我是否向关联数组传递一个指向
malloc
ed 值数组的单个 void *
指针并允许它在内部使用它(为此我们需要保证匹配的键数组大小) ?我可以通用地执行此操作吗,因为类型需要(?)已知才能索引到值数组中?
- 我是否在关联数组中有一个单独的
void * valuePtrs[]
,然后让这些指针指向 malloc
ed 值数组中的每个元素?这似乎可以避免需要了解具体类型?
- 我是否使用 C pseudo-generics 并因此允许
get()
到 return 特定值类型?当然,在这种情况下,唯一的好处是不必显式投射,例如MyStruct* value = (MyStruct*) get(...)
...数组元素仍需取消引用,因此开销相同?
而且,总的来说,上述最小化缓存未命中的方法是否有意义?
在这两种情况下,性能基本相同。
在第一个(void* 实现)中,您需要查找值 + 取消引用指针。所以这是两条指令。
在另一个实现中,您需要将索引乘以值的大小。所以这个实现也需要两条指令。
但是,第一个实现将更容易、更干净地实现。此外,数组是完全透明的;用户将不需要知道数组中的结构类型。
请参阅下面根据优缺点分类的解决方案(感谢 Ruben 帮助我思考)...我已经为我的用例实施了选项 2 和 5,这有点笼统;如果您需要非常具体的一次性数据结构,我推荐选项 4。选项 3 最灵活,但对代码来说很简单,但也是最慢的。选项 4 是最快的。选项 5 有点慢,但在数组大小上具有灵活性并且易于一般使用。
关联数组结构指向类型指针数组:
优点 不需要失败值,不需要显式转换,不需要数组的编译时大小
缺点 昂贵的双重解引用,需要通用库代码
关联数组结构包含 void *
个指针数组:
优点 不需要失败值,没有通用库代码
cons 代价高昂的双解引用,get()
之后的显式转换,如果不使用 VLA,则需要数组的编译时间大小
关联数组结构指向 void *
个值的数组:
优点 没有通用库代码,不需要数组的编译时大小
cons 代价高昂的三次 deref,get()
之后的显式转换,需要偏移计算,这需要显式传入 sizeof 值
关联数组结构包含类型值数组:
优点 便宜的单个 deref,不需要显式转换,键和条目连续分配
cons 需要通用库代码,必须提供失败值,如果不使用 VLA,则需要数组的编译时大小
关联数组结构指向类型值数组:
优点 不需要显式转换,灵活的数组大小
cons 昂贵的双 deref,需要通用库代码,必须提供失败值,如果不使用 VLA,则需要数组的编译时间大小
假设我有一个由 unsigned int
键控的关联数组;值可以是任何固定大小的类型。有一些预定义的最大数量。实例数。
API 用法示例:MyStruct * valuePtr = get(1234);
和 put(6789, &myStructInstance);
...基本。
我希望在从该数组中快速随机读取条目时最大程度地减少缓存未命中,因此我预先malloc(sizeof(MyType) * MAX_ENTRIES)
以尽可能确保引用的位置。
泛型对于值数组很重要。我看过 C pseudo-generics,但为了简单起见更喜欢 void *
;但是,不确定这是否与性能目标不一致。最后,我想知道什么对性能最好。
我应该如何实现我的关联数组以提高性能?到目前为止的想法...
- 我是否向关联数组传递一个指向
malloc
ed 值数组的单个void *
指针并允许它在内部使用它(为此我们需要保证匹配的键数组大小) ?我可以通用地执行此操作吗,因为类型需要(?)已知才能索引到值数组中? - 我是否在关联数组中有一个单独的
void * valuePtrs[]
,然后让这些指针指向malloc
ed 值数组中的每个元素?这似乎可以避免需要了解具体类型? - 我是否使用 C pseudo-generics 并因此允许
get()
到 return 特定值类型?当然,在这种情况下,唯一的好处是不必显式投射,例如MyStruct* value = (MyStruct*) get(...)
...数组元素仍需取消引用,因此开销相同?
而且,总的来说,上述最小化缓存未命中的方法是否有意义?
在这两种情况下,性能基本相同。
在第一个(void* 实现)中,您需要查找值 + 取消引用指针。所以这是两条指令。
在另一个实现中,您需要将索引乘以值的大小。所以这个实现也需要两条指令。
但是,第一个实现将更容易、更干净地实现。此外,数组是完全透明的;用户将不需要知道数组中的结构类型。
请参阅下面根据优缺点分类的解决方案(感谢 Ruben 帮助我思考)...我已经为我的用例实施了选项 2 和 5,这有点笼统;如果您需要非常具体的一次性数据结构,我推荐选项 4。选项 3 最灵活,但对代码来说很简单,但也是最慢的。选项 4 是最快的。选项 5 有点慢,但在数组大小上具有灵活性并且易于一般使用。
关联数组结构指向类型指针数组:
优点 不需要失败值,不需要显式转换,不需要数组的编译时大小
缺点 昂贵的双重解引用,需要通用库代码
关联数组结构包含 void *
个指针数组:
优点 不需要失败值,没有通用库代码
cons 代价高昂的双解引用,get()
之后的显式转换,如果不使用 VLA,则需要数组的编译时间大小
关联数组结构指向 void *
个值的数组:
优点 没有通用库代码,不需要数组的编译时大小
cons 代价高昂的三次 deref,get()
之后的显式转换,需要偏移计算,这需要显式传入 sizeof 值
关联数组结构包含类型值数组:
优点 便宜的单个 deref,不需要显式转换,键和条目连续分配
cons 需要通用库代码,必须提供失败值,如果不使用 VLA,则需要数组的编译时大小
关联数组结构指向类型值数组:
优点 不需要显式转换,灵活的数组大小
cons 昂贵的双 deref,需要通用库代码,必须提供失败值,如果不使用 VLA,则需要数组的编译时间大小