改善管理STD :: MAP对象的函数的执行时间,如何?

发布于 2025-02-13 22:52:29 字数 745 浏览 5 评论 0 原文

我有一个标题文件 header.hpp 在其中声明此功能:

#ifndef COMMON_HPP
#define COMMON_HPP

#include <string>
#include <map>

extern std::string func( const std::map <std::string, std::string>& generic_map, const std::string& feat_string )

#endif

并且该函数是在 .cpp 文件中定义的:

#include "header.hpp"

#include <string>
#include <map>

std::string func( const std::map <std::string, std::string>& map_s, const std::string& str )
 {
  if( map_s.find( str ) == map_s.end() ) 
   {
    throw std::runtime_error( "Error!" );
   }
  return map_s.at( str );
 }

基准测试注册的执行时间 292 292 NS 在2684415迭代中。您知道是否有任何方法可以改善功能以使其更快?

I have a header file header.hpp in which is declared this function:

#ifndef COMMON_HPP
#define COMMON_HPP

#include <string>
#include <map>

extern std::string func( const std::map <std::string, std::string>& generic_map, const std::string& feat_string )

#endif

and that function is defined in a .cpp file:

#include "header.hpp"

#include <string>
#include <map>

std::string func( const std::map <std::string, std::string>& map_s, const std::string& str )
 {
  if( map_s.find( str ) == map_s.end() ) 
   {
    throw std::runtime_error( "Error!" );
   }
  return map_s.at( str );
 }

Benchmarking registered an execution time of 292 ns in 2684415 iterations. Do you know if there is any way to improve the function in order to make it faster?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

怼怹恏 2025-02-20 22:52:30

鉴于代码有多小,实际上您可以对其进行很多更改,但是有几个:

  • 使用 iterator map :: find :: find()返回,您不需要调用 map :: at()之后。实际上,当您真的只想执行1次时,您实际上正在执行相同的搜索。


  • 返回对找到值的引用,以避免制作其副本( map :: at()无论如何还是返回引用)。您投掷的例外将确保返回的参考永远不会无效。

  • 如果您不需要 MAP 要排序,请使用 std :: unordered_map ,它通常比 > std :: map 用于插入和查找。

尝试以下操作:

const std::string& func( const std::map <std::string, std::string>& map_s, const std::string& str )
{
    auto iter = map_s.find( str );
    if (iter == map_s.end() ) 
        throw std::runtime_error( "Error!" );
    return iter->second;
}

话虽如此,如果您不介意投掷哪种错误消息,则可以用 map :: find() map :: at(),因为 map :: at()抛出自己的 std :: out_of_range 如果找不到键(这是 map ::的全部目的) at() vs map ::操作员[] ),例如:

const std::string& func( const std::map <std::string, std::string>& map_s, const std::string& str )
{
  return map_s.at( str );
}

Given how small the code is, there is really not many changes you can make to it, but there are a few:

  • use the iterator that map::find() returns, you don't need to call map::at() afterwards. You are actually performing the same search 2 times, when you really want to perform it only 1 time.

  • return a reference to the found value, to avoid making a copy of it (map::at() returns a reference anyway). The exception you are throwing will ensure the returned reference is never invalid.

  • If you don't need the map to be sorted, use std::unordered_map instead, which is usually faster than std::map for inserts and lookups.

Try this:

const std::string& func( const std::map <std::string, std::string>& map_s, const std::string& str )
{
    auto iter = map_s.find( str );
    if (iter == map_s.end() ) 
        throw std::runtime_error( "Error!" );
    return iter->second;
}

That being said, if you don't mind what kind of error message is thrown, you can replace map::find() with map::at(), since map::at() throws its own std::out_of_range exception if the key is not found (that is the whole purpose of map::at() vs map::operator[]), eg:

const std::string& func( const std::map <std::string, std::string>& map_s, const std::string& str )
{
  return map_s.at( str );
}
那支青花 2025-02-20 22:52:30

首先,它返回 std :: String 而不是 const std :: string&amp; ,它是慢的(涉及复制)。

同样,当前编译器中的例外非常昂贵。他们围绕他们的一种可能的方法是返回 const std :: string*和返回 nullptr 在没有找到元素的情况下(还删除了双重查找):

const std::string* func( const std::map <std::string, std::string>& map_s, const std::string& str )
 {
  auto it = map_s.find( str );
  if( it == map_s.end() ) 
   {
    return nullptr;
   }
  return &(it->second);
 }

高级技术正在继续通过样式,可以进一步优化您的代码(并为您节省分支)。在这种情况下,您没有返回某些内容,而是通过结果应完成的操作:

template<typename Found, typename NotFound>
void func( const std::map <std::string, std::string>& map_s, const std::string& str Found found, NotFound notFound)
{
  auto it = map_s.find( str );
  if( it == map_s.end() ) 
  {
    return notFound();
  }
  return found(it->second);
}

用法:

std::map<std::string, std::string> m;
std::string s;
func(m, s, [&](const std::string& elem) {
  /* found - do something with elem */
}, [&]() {
  /* not found - error handler */
});

这就像您拥有自己的控制结构一样。当然,您可以使返回类型与void不同(如果您想“隧道”找到(elem) notfound())的结果。如果您愿意,可以在多个函数的调用中共享错误处理程序(在这种情况下,您将其存储在变量中,例如 const auto onnotfound = [&amp;]()()(){ /* .. 。 */}; )。当然,当您进行优化时,您可能会将lambda的捕获限制为必要的。

First, it returns std::string instead of const std::string&, which is slow (involves a copy).

Also, exceptions in current compilers are very expensive. A possible way around them is to return a const std::string* and return nullptr in case element is not found (also removed double lookup):

const std::string* func( const std::map <std::string, std::string>& map_s, const std::string& str )
 {
  auto it = map_s.find( str );
  if( it == map_s.end() ) 
   {
    return nullptr;
   }
  return &(it->second);
 }

An advanced technique is continuation passing style, which can further optimize your code (and save you a branching). In this case, instead of returning something, you pass what should be done with the result:

template<typename Found, typename NotFound>
void func( const std::map <std::string, std::string>& map_s, const std::string& str Found found, NotFound notFound)
{
  auto it = map_s.find( str );
  if( it == map_s.end() ) 
  {
    return notFound();
  }
  return found(it->second);
}

Usage:

std::map<std::string, std::string> m;
std::string s;
func(m, s, [&](const std::string& elem) {
  /* found - do something with elem */
}, [&]() {
  /* not found - error handler */
});

This is like if you had your own control structures. Of course, you could make the return type different from void (in case you'd like to 'tunnel' the result of found(elem) and notFound()). If you'd like, you can share e.g. the error handler among multiple calls to the function (in which case, you store it in a variable, like const auto onNotFound = [&]() { /* ... */ };). Of course, when you're optimizing, you might limit the capture of the lambda to what's necessary.

北恋 2025-02-20 22:52:29

您正在向地图上进行两个查找以获取所寻求的元素:

这是一个立即的改进:

std::string func( const std::map <std::string, std::string>& map_s, const std::string& str )
 {
    auto itor = map_s.find(str);
    if (itor == map_s.end())
    {
      throw std::runtime_error( "Error!" );
    }
    return itor->second;
 }

您可以做出的另一个改进是使用a std :: unordered_map 而不是 std :: map 。如果您不需要按任何排序或一致的顺序列举地图中的密钥,则应使用unordered_map,这是插入和查找的o(1)。有序的地图可能具有O(LG N)运行时,用于插入和查找。 (即集合的哈希表与二进制树实现)。 std :: unordered_map std :: map 具有几乎相同的接口,因此这是一个简单的替换。

You're doing two lookups into the map to get the element you seek:

Here's an immediate improvement:

std::string func( const std::map <std::string, std::string>& map_s, const std::string& str )
 {
    auto itor = map_s.find(str);
    if (itor == map_s.end())
    {
      throw std::runtime_error( "Error!" );
    }
    return itor->second;
 }

The other improvement you can make is to use a std::unordered_map instead of std::map. If you don't need to enumerate keys in the map in any sorted or consistent order, you should use unordered_map, which is O(1) for insertion and lookup. The ordered map will likely have O(lg N) runtime for insertion and lookup. (i.e. hash table vs binary tree implementations for the collections). std::unordered_map and std::map have near identical interfaces, so it's an easy drop-in replacement.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文