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

时间:2013-01-14 19:47:10

标签: c# performance algorithm c++-cli string-conversion

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

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

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

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

String ^StringTools::MbCharToStr ( const char *Source ) 
{
   String ^str;

   if( (Source == NULL) || (Source[0] == '\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;
}

4 个答案:

答案 0 :(得分:5)

您可以使用输入字符串中的每个字符来提供trie结构。在叶子上,有一个.NET字符串对象。然后,当您以前看到的char*进入时,您可以快速找到现有的.NET版本而无需分配任何内存。

的伪代码:

  • 从一个空的特里开始,
  • 通过搜索特里来处理字符*,直到你不能再进一步
  • 添加节点,直到整个char *已编码为节点
  • 在叶子上,附加一个实际的.NET字符串

这个其他问题的答案应该让你开始:How to create a trie in c#

答案 1 :(得分:3)

  

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

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

存储映射,进行比较等有一些开销。但是,这可以通过显着减少内存使用(您可以重用托管字符串实例)以及保存内存分配(calloc /)来缓解。自由)。此外,使用malloc代替calloc可能会(非常小)改进,因为您在调用MultiByteToWideChar之前不需要将内存清零。

答案 2 :(得分:2)

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

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

也就是说,听起来好像项目的其余部分是C#,你应该使用.NET BCL类库来实现这一点而不是在C ++ / CLI中并排组装,仅用于转换字符串。这就是System.Text.Encoding的用途。

我怀疑你在这里使用的任何缓存数据结构都会产生重大影响。

哦,不要忽略MultiByteToWideChar的结果 - 不仅你不应该向void投射任何东西,你在MultiByteToWideChar失败的事件中有未定义的行为。

答案 3 :(得分:1)

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