Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/295.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 优化代码,使其执行更快_C#_String_Optimization - Fatal编程技术网

C# 优化代码,使其执行更快

C# 优化代码,使其执行更快,c#,string,optimization,C#,String,Optimization,如何优化以下代码,使其执行更快 static void Main(string[] args) { String a = "Hello "; String b = " World! "; for (int i=0; i<20000; i++) { a = a + b; } Console.WriteLine(a); } static void Main(字符串[]args) { 字符串a=“你好”; String b=

如何优化以下代码,使其执行更快

static void Main(string[] args) 
{
    String a = "Hello ";
    String b = " World! ";
    for (int i=0; i<20000; i++) 
    {
        a = a + b;
    }
    Console.WriteLine(a);
} 
static void Main(字符串[]args)
{
字符串a=“你好”;
String b=“世界!”;

对于(int i=0;i,由于其输出是预先确定的,因此如果您只是硬编码循环生成的文本值,它将运行得更快。

来自文档:

性能注意事项

Concat和AppendFormat方法都将新数据连接到现有字符串或StringBuilder对象。字符串对象连接操作总是从现有字符串和新数据创建一个新对象。StringBuilder对象维护一个缓冲区以容纳新数据的连接。新数据被附加到字符串的末尾缓冲区(如果房间可用);否则,将分配一个新的、更大的缓冲区,将原始缓冲区中的数据复制到新缓冲区,然后将新数据追加到新缓冲区

字符串或StringBuilder对象的连接操作的性能取决于内存分配的频率。字符串连接操作始终分配内存,而StringBuilder连接操作仅在StringBuilder对象缓冲区太小而无法容纳新数据时分配内存。因此实际上,如果连接了固定数量的字符串对象,则字符串类更适合用于连接操作。在这种情况下,编译器甚至可以将单个连接操作组合成单个操作。如果连接了任意数量的字符串,则StringBuilder对象更适合用于连接操作连接;例如,如果循环连接随机数目的用户输入字符串。

static void Main(字符串[]args){
字符串a=“你好”;
String b=“世界!”;
StringBuilder结果=新StringBuilder(a.长度+b.长度*20000);
结果:追加(a);

对于(int i=0;i它可能是IO主导的(将输出写入控制台或文件将是最慢的部分),因此可能不会从高度优化中受益。简单地消除明显的悲观情绪就足够了

一般来说,不要创建临时对象。循环的每次迭代都会创建一个临时字符串,覆盖
a
中的整个前一个字符串和
b
中的字符串值,因此每次循环都要执行多达20000倍于
b
长度的操作。即使如此,对c来说也只有30亿字节opy,因此在现代机器上应该在不到一秒钟的时间内完成(假设运行时对目标硬件使用正确的操作)。将160008个字符转储到控制台可能需要更长的时间

一种技术是使用生成器或缓冲区创建更少的临时对象,而不是使用
StringBuilder
在内存中创建一个长字符串,然后将其复制到字符串,然后输出该字符串

但是,您可以更进一步,通过直接写入输出,而不是使用
Console创建任何临时字符串或缓冲区来实现相同的功能。改为在循环中写入
。这将删除两个复制操作(字符串b被复制到缓冲区,然后缓冲区被复制到字符串对象,然后字符串的数据被复制到输出缓冲区;最后的复制操作是控制台内部的操作。写入so在C#中是无法避免的),但需要更多的操作系统调用,因此可能更快,也可能更快

另一种常见的优化方法是展开循环。因此,与一个循环有一行写一个“世界”,循环20000次不同,你可以有(比如)五行写一个“世界!”每次循环4000次。通常只有当递增和测试循环变量的成本比循环中的成本高时,才值得单独进行,但这可能导致其他优化

部分展开循环后,您可以将循环中的代码组合起来,通过一次对
控制台的调用编写五个或十个“World!”s。write
,这将节省一些时间,因为您只进行了五分之一的系统调用


写入控制台,在cmd窗口中,它似乎受到控制台窗口速度的限制:

(100次运行的时间单位为秒)

写入文件时,不同技术的时间不同:

     205.0000000 - concat
       9.7031250 - direct
       1.0781250 - direct writing b x10
       0.5000000 - builder
       0.4843750 - direct writing b x100
       0.4531250 - builder writing b x100
由此可以得出两个结论:

如果您在cmd.exe窗口中写入控制台,大多数改进都无关紧要。您确实需要将系统作为一个整体进行分析,并且(除非您试图减少CPU的能耗),优化一个组件超出系统其余部分的能力是没有意义的

虽然显然要做更多的工作—复制更多的数据并调用相同数量的函数,但StringBuilder方法速度更快。这意味着与非托管语言中的等效方法相比,每次调用Console.Write都有相当高的开销

在Win XP上使用gcc C99写入文件:

    0.375 - direct ( fputs ( b, stdout ) 20000 times )
    0.171 - direct unrolled ( fputs ( b x 100, stdout ) 200 times )
    0.171 - copy to b to a buffer 20000 times then puts once
在C中,系统调用的较低成本使其接近IO绑定,而不是受.net运行时边界的限制。因此,在优化.net时,托管/非托管边界变得非常重要

static void Main(string[] args) 
{
    const String a = "Hello " +
        /* insert string literal here that contains " World! " 20000 times. */ ;
    Console.WriteLine(a);
}

我不敢相信他们在学校里教的都是这样的胡说八道。没有一个真实世界的例子说明你为什么要这样做,更不用说优化它了。所有这些教的都是如何对一个没有任何用处的程序进行微优化,而这对学生作为程序员/开发人员的健康会产生反作用。

我想知道,这会有什么影响吗斯特

static void Main(string[] args) {
    String a = "Hello ";
    String b = " World! ";
    int worldCount = 20000;
    StringBuilder worldList = new StringBuilder(b.Length * worldCount);
    worldList.append(b);
    StringBuilder result = new StringBuilder(a.Length + b.Length * worldCount);
    result.Append(a);

    while (worldCount > 0) {

       if ((worldCount & 0x1) > 0) {  // Fewer appends, more ToStrings.
          result.Append(worldList);   // would the ToString here kill performance?
       }
       worldCount >>= 1;
       if (worldCount > 0) {
          worldList.Append(worldList);
       }
    }

    Console.WriteLine(result.ToString());
}

取决于字符串对象中的内容。如果它们在内部只有一个以null结尾的字符串,那么可以通过将字符串的长度存储在某个位置进行优化。此外,如果您只是输出到stdout,则将输出调用移到内部会更有意义
static void Main(string[] args) 
{
    const String a = "Hello " +
        /* insert string literal here that contains " World! " 20000 times. */ ;
    Console.WriteLine(a);
}
static void Main(string[] args) {
    String a = "Hello ";
    String b = " World! ";
    int worldCount = 20000;
    StringBuilder worldList = new StringBuilder(b.Length * worldCount);
    worldList.append(b);
    StringBuilder result = new StringBuilder(a.Length + b.Length * worldCount);
    result.Append(a);

    while (worldCount > 0) {

       if ((worldCount & 0x1) > 0) {  // Fewer appends, more ToStrings.
          result.Append(worldList);   // would the ToString here kill performance?
       }
       worldCount >>= 1;
       if (worldCount > 0) {
          worldList.Append(worldList);
       }
    }

    Console.WriteLine(result.ToString());
}
    static void Main(string[] args)
    {
        String a = "Hello ";
        String b = " World! ";

        System.IO.MemoryStream ms = new System.IO.MemoryStream(20000 * b.Length + a.Length);
        System.IO.StreamWriter sw = new System.IO.StreamWriter(ms);

        sw.Write(a);
        for (int i = 0; i < 20000; i++)
        {
            sw.Write(b);
        }

        ms.Seek(0,System.IO.SeekOrigin.Begin);
        System.IO.StreamReader sr = new System.IO.StreamReader(ms);
        Console.WriteLine(sr.ReadToEnd());
    }
static void Main(string[] args) 
{
    Console.Write("Hello ");
    for (int i=0; i<20000; i++)
       Console.Write(" World! ");
    Console.Write(Environment.NewLine);
}
static void Main(string[] args) 
{
   String a = "Hello "; 
   String b = " World! ";

   int it = 20000;
   char[] result = new char[a.Length + it*b.Length];

   a.ToCharArray().CopyTo(result, 0);

   for (int i = 0; i < it; i++) 
      b.ToCharArray().CopyTo(result, a.Length + i * b.Length);

   Console.WriteLine(result);    
}