【问题标题】:How to optimize string copying (memory allocation)?如何优化字符串复制(内存分配)?
【发布时间】:2011-09-19 23:40:33
【问题描述】:

我正在制作一个程序(想想:Launchy 之类的东西),它或多或少会遍历一堆字符串并根据某些标准对它们进行排序。

我将结果存储在vector<SearchSuggestion> 中,其中结构当前定义如下:

struct SearchSuggestion
{
    std::string path;
    int tag;
};

在我的程序中,我复制了很多结构(以及字符串),因为需要操作大量的文件路径等等。

虽然这会导致发布模式出现明显但很小的延迟,但它极大地减慢了我的程序的调试速度(即,击键之间有几秒钟的暂停)。寻找原因,我发现几乎所有时间都花在了以下堆栈跟踪上:

ntdll.dll!RtlCompareMemoryUlong()   
ntdll.dll!RtlpAllocateHeap()    
ntdll.dll!RtlAllocateHeap()     
ntdll.dll!RtlDebugAllocateHeap()    
ntdll.dll!string "Enabling heap debug options\n"()  
ntdll.dll!RtlAllocateHeap()     
msvcr90d.dll!_heap_alloc_base(unsigned __int64)     C
msvcr90d.dll!_heap_alloc_dbg_impl(unsigned __int64, int, const char *, int, int *)
msvcr90d.dll!_nh_malloc_dbg_impl(unsigned __int64, int, int, const char *, int, int *)
msvcr90d.dll!_nh_malloc_dbg(unsigned __int64, int, int, const char *, int)
msvcr90d.dll!malloc(unsigned __int64)
msvcr90d.dll!operator new(unsigned __int64)
MyProgram.exe!std::_Allocate<wchar_t>(unsigned __int64, wchar_t *)
MyProgram.exe!std::allocator<wchar_t>::allocate(unsigned __int64)
MyProgram.exe!std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >::_Copy(unsigned __int64, unsigned __int64)
MyProgram.exe!std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >::_Grow(unsigned __int64, bool)
MyProgram.exe!std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >::assign(const std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> > &, unsigned __int64, unsigned __int64)
MyProgram.exe!std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >(const std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> > &)
MyProgram.exe!SearchSuggestion::SearchSuggestion(const SearchSuggestion &)
MyProgram.exe!std::_Construct<SearchSuggestion,SearchSuggestion>(SearchSuggestion *, const SearchSuggestion &)
MyProgram.exe!std::allocator<SearchSuggestion>::construct(SearchSuggestion *, const SearchSuggestion &)
MyProgram.exe!std::_Uninit_copy<SearchSuggestion * __ptr64,SearchSuggestion * __ptr64,std::allocator<SearchSuggestion> >(SearchSuggestion *, SearchSuggestion *, SearchSuggestion *, std::allocator<SearchSuggestion> &, std::_Nonscalar_ptr_iterator_tag, std::_Nonscalar_ptr_iterator_tag)
MyProgram.exe!stdext::unchecked_uninitialized_copy<SearchSuggestion * __ptr64,SearchSuggestion * __ptr64,std::allocator<SearchSuggestion> >(SearchSuggestion *, SearchSuggestion *, SearchSuggestion *, std::allocator<SearchSuggestion> &)
MyProgram.exe!std::_Uninit_move<SearchSuggestion * __ptr64,SearchSuggestion * __ptr64,std::allocator<SearchSuggestion>,std::_Undefined_move_tag>(SearchSuggestion *, SearchSuggestion *, SearchSuggestion *, std::allocator<SearchSuggestion> &, std::_Undefined_move_tag, std::_Undefined_move_tag)
MyProgram.exe!stdext::_Unchecked_uninitialized_move<SearchSuggestion * __ptr64,SearchSuggestion * __ptr64,std::allocator<SearchSuggestion> >(SearchSuggestion *, SearchSuggestion *, SearchSuggestion *, std::allocator<SearchSuggestion> &)
MyProgram.exe!std::vector<SearchSuggestion,std::allocator<SearchSuggestion> >::_Umove<SearchSuggestion * __ptr64>(SearchSuggestion *, SearchSuggestion *, SearchSuggestion *)
MyProgram.exe!std::vector<SearchSuggestion,std::allocator<SearchSuggestion> >::_Insert_n(std::_Vector_const_iterator<SearchSuggestion,std::allocator<SearchSuggestion> > *, unsigned __int64, const SearchSuggestion &)
MyProgram.exe!std::vector<SearchSuggestion,std::allocator<SearchSuggestion> >::insert(std::_Vector_const_iterator<SearchSuggestion,std::allocator<SearchSuggestion> > *, const SearchSuggestion &)
MyProgram.exe!std::vector<SearchSuggestion,std::allocator<SearchSuggestion> >::push_back(const SearchSuggestion &)
MyProgram.exe!Appender<std::vector<SearchSuggestion,std::allocator<SearchSuggestion> > >(const wchar_t *, _tfinddata *, void *)
MyProgram.exe!EnumMatches(const std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> > &, const std::vector<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> >,std::allocator<std::basic_string<wchar_t,std::char_traits<wchar_t>,std::allocator<wchar_t> > > > &, int (const wchar_t *, _tfinddata *, void *)*, void *, int)
...

所以很明显,复制std::string 花费的时间太长,可能是由于引用的局部性差。

所以现在我的问题很简单:

如何提高分配大量小字符串的性能?

【问题讨论】:

  • 试试 VS2010。通过移动语义和小字符串优化,我很想相信它会表现得更好。
  • @KerrekSB:整个 IDE 在 2010 年的运行速度(加起来)比我在程序中遇到的还要糟糕,所以这对这种情况没有帮助。 :(
  • EnumMatches()Appender() 到底在做什么?知道这一点可能会更容易提出避免复制字符串的建议(例如,将副本缓存在某处是可能的并且适当的)。
  • @MichaelBurr: Appender 只有 4 行——它从给定的字符串创建一个 SearchSuggestion 并将结果推送到 vector。然而,EnumMatches 是程序的主要部分——它枚举了一堆匹配项(根据给定的参数确定)并为每个匹配项调用给定的回调。他们都做了一些字符串复制。还有一个没有显示的RankMatches——它也进行字符串复制,并返回一个包含一些其他排名数据的建议对的向量。我确实可能可以缓存字符串,但这会使程序的模块化程度降低。

标签: c++ string visual-studio-2008 visual-c++ stl


【解决方案1】:

一种方法是停止将它们按值放入SearchSuggestion 结构中。而是给每个SearchSuggestion 一个句柄,指向代表路径的std::string

struct SearchSuggestion {
  int pathId;
  int tag;
};

这将使向量内的复制更加有效,因为它只会复制简单的ints 而不是复杂的std::string 值。

然后您可以使用std::map&lt;int, std::string&gt; 结构将路径ID 映射到真实路径。

【讨论】:

  • ooooo...有趣的想法!我实际上也可以存储对地图的引用,以避免传递额外的参数...我会尝试一下,谢谢! +1(编辑:不确定这是否会是一个微不足道的改变,因为那样我必须确保我不会在向量之前释放后备存储......不过会尝试。)
  • 这有几个变种;最激进的使用一个巨大的char[] 并使pathId 成为该数组的size_t 偏移量。基本主题是您通常不需要修改字符串,唯一的变化是分配一个完整的新值。
【解决方案2】:

您可以尝试使用 Boost.Flyweight。我不保证它会起作用——不复制字符串所节省的时间可能会花在检查该字符串是否尚未存储上——但你可以试一试。

另一种选择是将其转换为boost::shared_ptr&lt;std::string&gt;。现在只需要复制一个指针(因此成本实际上为空),但现在实际访问该字符串时会增加成本(但这可能不是什么大问题)。

尝试这两个应该不难,看看哪一个产生更好的结果。

【讨论】:

  • 对不起,但我认为您的答案的两个部分都是其他两个答案的重复。 :\
【解决方案3】:

最完美的答案是:不要分配和复制大量字符串。改为使用指向常量字符串或符号的共享指针。

Malloc() 和 strcpy() 只是很慢,句号,并且复制字符串总是需要 O(n) 操作。在实时代码中,最好尽可能避免分配。

【讨论】:

  • 这真的有帮助吗?这将是一个很大的设计更改,我不确定这是否是个好主意...无论如何,共享指针都需要额外的堆分配,而且看起来不像是 复制这个问题(与分配本身相反,特别是因为它处于调试模式)所以无论哪种方式,我都会获得两倍于我需要的分配,并且参考位置非常糟糕......
  • 另外,我不得不提一下:每个字符串被复制的次数是固定的(大约是少数)。但是字符串的 number 是可变的。虽然我可以避免复制它们,但我无法避免第一次分配它们(因为涉及到路径操作),所以最多只能让我的程序更快通过一个常数因子(大约 2-5 倍),这无论如何都是微不足道的,考虑到我的数据集现在相当小,并且可以很容易地以更大的因子增长。
  • @Mehrdad Jared 有一个很好的例子来说明我所说的共享指向字符串的指针是什么意思,所以我赞成他的回答。 =)
【解决方案4】:
猜你喜欢
  • 2017-10-29
  • 2016-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-22
  • 2012-07-10
  • 2011-03-22
  • 2021-06-11
相关资源
最近更新 更多