优化数百万个char*到字符串的转换



我有一个应用程序,需要接受数百万char*作为输入参数(通常字符串少于512个字符(unicode)),并转换并存储为。net字符串。

它最终成为我的应用程序性能的真正瓶颈。我想知道是否有一些设计模式或想法使它更有效。

有一个关键的部分,让我觉得它可以改进:有很多重复。假设有100万个对象进入,可能只有50个独特的char*模式。

为了记录,这里是我用来将char*转换为字符串的算法(该算法在c++中,但项目的其余部分在c#中)

String ^StringTools::MbCharToStr ( const char *Source ) 
{
   String ^str;
   if( (Source == NULL) || (Source[0] == '') )
   {
      str = gcnew String("");
   }
   else
   {
      // Find the number of UTF-16 characters needed to hold the
      // converted UTF-8 string, and allocate a buffer for them.
      const size_t max_strsize = 2048;
      int wstr_size = MultiByteToWideChar (CP_UTF8, 0L, Source, -1, NULL, 0);
      if (wstr_size < max_strsize)
      {
         // Save the malloc/free overhead if it's a reasonable size.
         // Plus, KJN was having fits with exceptions within exception logging due
         // to a corrupted heap.
         wchar_t wstr[max_strsize];
         (void) MultiByteToWideChar (CP_UTF8, 0L, Source, -1, wstr, (int) wstr_size);
         str = gcnew String (wstr);
      }
      else
      {
         wchar_t *wstr = (wchar_t *)calloc (wstr_size, sizeof(wchar_t));
         if (wstr == NULL) 
            throw gcnew PCSException (__FILE__, __LINE__, PCS_INSUF_MEMORY, MSG_SEVERE);
         // Convert the UTF-8 string into the UTF-16 buffer, construct the
         // result String from the UTF-16 buffer, and then free the buffer.
         (void) MultiByteToWideChar (CP_UTF8, 0L, Source, -1, wstr, (int) wstr_size);
         str = gcnew String ( wstr );
         free (wstr);
      }
   }
   return str;
}

可以使用输入字符串中的每个字符来提供一个tree结构。最后,有一个。net字符串对象。然后,当您之前看到的char*出现时,您可以快速找到现有的。net版本,而无需分配任何内存。

伪代码:

  • 以空树开始,
  • 通过搜索树来处理char*,直到无法继续
  • 添加节点,直到整个char*被编码为节点
  • 在叶子,附加一个实际的。net字符串
这个问题的答案应该让你开始:如何在c#中创建一个树

有一个关键的部分,让我觉得它可以改进:有很多重复。假设有100万个对象传入,可能只有50个独特的char*模式。

如果是这种情况,您可能需要考虑将"找到的"模式存储在映射中(例如使用std::map<const char*, gcroot<String^>>[尽管您需要const char*的比较器),并使用它返回先前转换的值。

存储映射、进行比较等都有开销。然而,这可以通过显著减少内存使用(您可以重用托管字符串实例)以及节省内存分配(calloc/free)来缓解。此外,使用malloc代替calloc可能是一个(非常小的)改进,因为您不需要在调用MultiByteToWideChar之前将内存归零。

我认为你可以在这里做的第一个优化是让你第一次尝试调用MultiByteToWideChar开始一个缓冲区而不是一个空指针。由于指定了CP_UTF8,因此MultiByteToWideChar必须遍历整个字符串以确定期望的长度。如果有某个长度比你的绝大多数字符串都长,你可以乐观地考虑在堆栈上分配一个相同大小的缓冲区;如果失败了,就进行动态分配。也就是说,如果您的if/else块位于if/else之外,则移动第一个分支。

您还可以通过计算一次源字符串的长度并显式传递它来节省一些时间——这样MultiByteToWideChar就不必在每次调用它时都执行strlen

也就是说,听起来如果你的项目的其余部分是c#,你应该使用。net BCL类库来做这件事,而不是在c++/CLI中为了转换字符串的唯一目的而使用并行汇编。这就是System.Text.Encoding的作用

我怀疑你在这里使用的任何一种缓存数据结构都不会有什么显著的不同。

哦,不要忽略MultiByteToWideChar的结果——您不仅不应该将任何内容强制转换到void,而且在MultiByteToWideChar失败的情况下,您会得到未定义的行为。

我可能会使用基于三元树结构或类似结构的缓存,并在将单个字符转换为。net表示之前查找输入字符串,看看它是否已经转换。

最新更新