代码之家  ›  专栏  ›  技术社区  ›  Colin Burnett

C#中的字符串串联与内部字符串

  •  3
  • Colin Burnett  · 技术社区  · 17 年前

    我知道这个问题 has done 但我有一个稍微不同的想法。一些人指出,这是过早的优化,如果我只是为了实用而问,这是完全正确的。我的问题根源于一个实际问题,但我仍然很好奇。


    复制字符串 投入其中 重新分配 根据需要选择阵列。

    但是,我的代码有很多重复字符串(“createtable[”、“GO\n”等),这意味着我可以利用它们 being interned 但如果我使用StringBuilder,则不会,因为它们每次都会被复制。唯一的变量本质上是表名,并且已经作为字符串存在于内存中的其他对象中。

    因此,据我所知,在读入数据并创建包含模式信息的对象之后,所有字符串信息都可以通过interning重用,是吗?

    假设是这样,那么字符串列表或LinkedList是否会更快,因为它们保留指向内部字符串的指针?然后,只需调用String.Concat()一次,就可以为整个字符串分配正确长度的内存。

    列表必须重新分配字符串[]的内部指针,链表必须创建节点和修改指针,所以它们不是“自由”的,但如果我 连接数千个内部字符串 这样他们看起来会更有效率。

    现在我想我可以为每个SQL语句提供一些关于字符计数的启发式方法&对每种类型进行计数,大致了解并预先设置我的StringBuilder容量,以避免重新分配其char[],但我必须以合理的幅度进行调整,以降低重新分配的可能性。

    • 字符串拼接
    • LinkedList<字符串>内接弦
    • 具有容量启发式的StringBuilder

    作为一个 单独问题 (我可能不总是去磁盘)到上面的问题:单个StreamWriter到输出文件会更快吗?或者,使用列表或链接列表,然后将它们从列表写入文件,而不是首先在内存中串联。

    应要求, the reference (.NET 3.5)到MSDN。它说: 如果有空间,新数据将附加到缓冲区的末尾;否则,将分配一个新的、更大的缓冲区,将原始缓冲区的数据复制到新缓冲区,然后将新数据附加到新缓冲区 对我来说,这意味着一个char[]被重新分配以使其更大(这需要将旧数据复制到调整大小的数组中),然后追加。

    7 回复  |  直到 9 年前
        1
  •  3
  •   paquetp    17 年前

    如果我实现这样的东西,我永远不会构建StringBuilder(或脚本的任何其他内存缓冲区)。

    FileStream f = new FileStream("yourscript.sql");
    foreach (Table t in myTables)
    {
        f.write("CREATE TABLE [");
        f.write(t.ToString());
        f.write("]");
        ....
    }
    

    然后,您就不再需要脚本的内存中表示,以及所有字符串的复制。

    意见?

        2
  •  3
  •   peterchen    17 年前

    为了你的 ,Win32具有 WriteFileGather 函数,它可以有效地将一组(插入的)字符串写入磁盘,但只有在异步调用时,它才会产生显著的差异,因为磁盘写入将掩盖除超大连接以外的所有连接。

    为了你的 主要问题

    纯粹作为练习,一些人估计:构建1MB的缓冲区将扫描大约3MB的内存(1MB源、1MB目标、1MB源) 在重新分配期间进行复制)。

    是的,列表实现可能更快,如果您的缓冲区大一个数量级,那么差异就很重要了。

    对于更常见的小字符串情况,算法增益可以忽略不计,并且很容易被其他因素抵消:StringBuilder代码可能已经在代码缓存中,并且是微优化的可行目标。此外,如果最终字符串适合初始缓冲区,则在内部使用字符串意味着根本没有副本。

    使用链表还可以将重新分配问题从O(字符数)减少到O(段数)-字符串引用列表面临与字符串相同的问题!


    不过,如果能比较一下这两种想法,以及当列表开始变快时,那将是一件有趣的事情。

        3
  •  2
  •   Bob King    17 年前

    根据我的经验,对于大量的字符串数据,我正确分配的StringBuilder比其他任何东西都好。浪费一些内存是值得的,甚至,为了防止重新分配,将您的估计超出20%或30%。我目前没有用我自己的数据来备份它的硬数据,但是看看 this page for more .

    However, as Jeff is fond of pointing out, don't prematurely optimize!

    编辑:正如@Colin Burnett所指出的,Jeff进行的测试与Brian的测试不一致,但是链接Jeff的帖子的重点是一般的过早优化。杰夫页面上的几位评论员指出了他的测试存在的问题。

        4
  •  1
  •   Brian Rasmussen    17 年前

    StringBuilder 使用 String 内部。 一串 实际上,在 System 组装,这就是为什么 可以建立在它之上。你可以

    字符串内部处理适用于可在编译时识别的字符串。因此,如果您在执行过程中生成了大量字符串,它们将不会被插入,除非您自己通过对字符串调用interning方法来执行。

    "SOMESTRINGA" "SOMESTRINGB" 将是两个不同的字符串,即使他们被拘留。

        5
  •  1
  •   Scott Wisniewski    17 年前

    如果所有(或大部分)被连接的字符串都是内部的,那么您的方案可能会给您带来性能提升,因为它可能会占用更少的内存,并且可以节省一些大型字符串副本。

    唯一的方法是使用这两种方法运行应用程序并测量结果。然而,除非您面临巨大的内存压力,并且需要一种节省字节的方法,否则我不会费心,只会使用字符串生成器。

        6
  •  1
  •   Guffa    17 年前

    A. StringBuilder char[] 字符串拼接 仅将内部字符串缓冲区作为常规字符串返回。

    字符串拼接 字符串拼接

    然而,为了获得一点透视图,你应该看看你试图优化的是什么。在您的程序中,需要花费的大部分时间是将数据实际写入磁盘,因此,即使您可以优化字符串处理,使其速度比使用 字符串拼接

        7
  •  0
  •   GregC Benjamin Baumann    17 年前

    关于字符串最慢的是malloc。在32位平台上,每个字符串需要4KB。考虑优化创建的字符串对象数。

    string varString1 = tableName;
    string varString2 = tableName;
    
    StringBuilder sb1 = new StringBuilder("const expression");
    sb1.Append(varString1);
    
    StringBuilder sb2 = new StringBuilder("const expression");
    sb2.Append(varString2);
    
    string resultingString = sb1.ToString() + sb2.ToString();
    

    如果perf如此重要的话,我甚至会让计算机通过依赖注入框架来评估对象实例化的最佳路径。

    推荐文章