std::map(和家庭)查找性能问题
std::map (and family) lookup performance issues
我正在编写一个位域抽象 class,它环绕着一块 32 位内存 (u32 = unsigned int) 并提供对该内存中各个位或范围的访问。
为了实现这一点,我使用了 std::map,其中唯一键是一个 指针 (不是 std::string),指向一个 C 字符数组,表示助记符,值是包含位域属性(如助记符、起始位置、长度、初始值和字段值)的结构。所有这些属性都是常量并在启动时定义,除了仅在基础 u32 值更改时更改的字段值。
(另请注意:我刚刚将助记符指针值重新用作唯一键)。
这在模拟器中使用,其中 getBitfieldValue()
,其中 returns 位域值(只读)每秒被调用多次。
在 VS 2015 update 3 下编译和分析代码(使用 -O2 和我能找到的任何速度优化),它表明 getBitfieldValue()
函数,并且通过扩展 std::find()
正在采取大约占总时间的 60-70% cpu...太慢了。
我尝试过使用其他地图实现,例如 Boost::flat_map
、google::dense_hash_map
或 std::unordered_map
,它们有些帮助,但最终还是太慢了(~50-60% ).
我的猜测是我将映射用于错误的目的,但考虑到只有 5-20 个位域映射(小查找大小),我不确定...它似乎太慢了。大部分时间也会花在查找相同的字段上。
相关class源代码可以在这里找到:BitfieldMap32
地图在启动时如何初始化的示例(运行 仅一次性):
struct Fields
{
static constexpr char * ADDR = "ADDR";
static constexpr char * SPR = "SPR";
};
ExampleClass() // constructor
{
// registerField(mnemonic, start position, length, initial value)
registerField(Fields::ADDR, 0, 31, 0);
registerField(Fields::SPR, 31, 1, 0);
}
以及如何访问字段值(只读):
// getFieldValue definition.
const u32 & BitfieldMap32_t::getFieldValue(const char* fieldName)
{
return mFieldMap.find(fieldName)->second.mFieldValue;
}
// Field access.
const u32 value = ExampleClassPointer->getFieldValue(Fields::ADDR)
关于如何减少查找时间有什么想法吗?还是我需要一起更改实现?
IIUC,使用字典(std::map
或 std::unordered_map
)是一个巨大的矫枉过正。也许你应该使用以下内容:
class 应该只是一个整数(或至多 std::bitset
)内部存储的包装器。
助记符应该是enum
s,不是std::string
s。
在内部,有一个 std::vector
有效地将每个 enum
值映射到 bitmask. (If you're using c++11 enum
s, see here 如何将 enum
值转换到std::vector
).
每个操作只需要取助记词,通过索引找到位掩码,并将其应用到内部存储。
我正在编写一个位域抽象 class,它环绕着一块 32 位内存 (u32 = unsigned int) 并提供对该内存中各个位或范围的访问。
为了实现这一点,我使用了 std::map,其中唯一键是一个 指针 (不是 std::string),指向一个 C 字符数组,表示助记符,值是包含位域属性(如助记符、起始位置、长度、初始值和字段值)的结构。所有这些属性都是常量并在启动时定义,除了仅在基础 u32 值更改时更改的字段值。 (另请注意:我刚刚将助记符指针值重新用作唯一键)。
这在模拟器中使用,其中 getBitfieldValue()
,其中 returns 位域值(只读)每秒被调用多次。
在 VS 2015 update 3 下编译和分析代码(使用 -O2 和我能找到的任何速度优化),它表明 getBitfieldValue()
函数,并且通过扩展 std::find()
正在采取大约占总时间的 60-70% cpu...太慢了。
我尝试过使用其他地图实现,例如 Boost::flat_map
、google::dense_hash_map
或 std::unordered_map
,它们有些帮助,但最终还是太慢了(~50-60% ).
我的猜测是我将映射用于错误的目的,但考虑到只有 5-20 个位域映射(小查找大小),我不确定...它似乎太慢了。大部分时间也会花在查找相同的字段上。
相关class源代码可以在这里找到:BitfieldMap32
地图在启动时如何初始化的示例(运行 仅一次性):
struct Fields
{
static constexpr char * ADDR = "ADDR";
static constexpr char * SPR = "SPR";
};
ExampleClass() // constructor
{
// registerField(mnemonic, start position, length, initial value)
registerField(Fields::ADDR, 0, 31, 0);
registerField(Fields::SPR, 31, 1, 0);
}
以及如何访问字段值(只读):
// getFieldValue definition.
const u32 & BitfieldMap32_t::getFieldValue(const char* fieldName)
{
return mFieldMap.find(fieldName)->second.mFieldValue;
}
// Field access.
const u32 value = ExampleClassPointer->getFieldValue(Fields::ADDR)
关于如何减少查找时间有什么想法吗?还是我需要一起更改实现?
IIUC,使用字典(std::map
或 std::unordered_map
)是一个巨大的矫枉过正。也许你应该使用以下内容:
class 应该只是一个整数(或至多
std::bitset
)内部存储的包装器。助记符应该是
enum
s,不是std::string
s。在内部,有一个
std::vector
有效地将每个enum
值映射到 bitmask. (If you're using c++11enum
s, see here 如何将enum
值转换到std::vector
).每个操作只需要取助记词,通过索引找到位掩码,并将其应用到内部存储。