【发布时间】:2017-09-12 12:25:06
【问题描述】:
这可能是一个愚蠢的问题,因为 std::set 已经有非常好的比较运算符,但我认为我可能会对我的特定用例进行优化,并希望确保我没有受到伤害我自己。
本质上,我有一个代价高昂的操作,它将 std::set& 作为输入。我正在缓存操作的结果,因此如果已经传入相同的输入,我就可以返回结果。这确实需要存储集合的副本(我在
std::map<std::set<std::string>, Result*>
,然后在每次调用操作时进行搜索。由于很可能会连续调用数千次相同的操作,所以我会说缓存的 std::set 找到 >99% 的时间。我最近尝试了我认为可能是一个小的改进,基于传入的字符串中某些字符无效的事实:我将 std::set 扁平化为单个字符串,组件字符串用'分隔: ' 特点。然后我的 std::map 变成了
std::map<std::string, Result*>
每次调用操作时,集合都会被展平并在缓存中搜索单个字符串。
实际上我对性能改进感到惊讶。我的测试运行使用包含 5 个字符串的 std::sets,每个字符串长度为 30 个字符,并且运行了 10,000,000 次搜索。在我的工作站上,每次运行的时间是
std::map<std::set<std::string>, Result*> : 138.8 seconds
std::map<std::string, Result> : 89.2 seconds
看起来,即使每次调用都需要展平集合的开销,第二种方法也是一个巨大的改进。我想我的问题是:为什么?我是否在这里做了一些潜在的坏事,而 std::set 的实现者故意避免了(即可能导致较大的字符串出现坏的堆碎片?)仅仅是因为集合中的各个字符串位于不同的位置并且必须单独比较?我是在踢自己的脚吗?在这种特定情况下,这种性能提升似乎太明显了。
【问题讨论】:
-
如果您在 99% 的情况下使用相同的参数调用函数,那么我会说调用者有问题,而不是函数本身有问题。无论如何,您不能在您的集合中添加某种
id,以便该方法只需要比较id而不是整个set吗?听起来你正在传递的集合并不经常改变。 -
我做了一点简化,函数的输入是 std::set 和要比较的 2 个单独的消息。该集合描述了在比较之前应用于消息的转换,并且它正在构建这个转换是昂贵的部分(应用它是微不足道的)。集合几乎总是不变,但消息几乎总是不同的。理想情况下,我会让调用者以某种方式获取转换的句柄,然后在调用比较时使用句柄而不是集合 - 不幸的是,这需要替换现有代码。
-
只要确保你的分隔符不能是实际字符串的一部分,你应该没问题。此外,无论何时,不要忘记使用 std::unordered_map 或 std::unordered_set 进行性能测试。然而,字符串并不总是存储在其中的最佳类型,因为您必须读取整个字符串才能生成哈希,而操作符
-
考虑到它是一个查找,
unordered_map可能更有效。此外,当使用字符串作为键时,不需要按字母顺序,首先比较字符串长度会更有效。 IE。将“z”排在“aa”之前。
标签: c++ string performance stl set