2017-01-17 1 views
0

Ich habe den folgenden Code:Wie dieser Code performanter machen

static void Main(string[] args) 
{ 
    int[] array = FillArray(); //Imagine FillArray() Fills the array with numbers from 0 to 100 
    string numbers = ""; 
    for(int i = 0;i<array.Count();i++) 
    { 
     numbers += i.ToString(); 
    } 

} 

Was Im versuchen zu tun, ist dieser Code performanter zu machen, vielleicht stattdessen ein For der Verwendung eines in C# Methode gebaut verwenden, Erweiterung Methode, die dies macht, ohne diese For 100 Mal zu wiederholen.

Edit: Es ist eine breite Frage, Natürlich haben Sie Ihre eigene Antwort in Bezug auf Ihr Wissen können wir sagen, dass alle Antworten, die den Timer dieses Algorithmus schlagen, ausreichende Antwort für die Frage an sich ist.

Stellen Sie sich vor, mit diesem Algorithmus 1.000.000.000 Mal zu iterieren, dann können wir mit konkreten Zahlen über verschiedene Lösungen vergleichen.

+0

'Enumerable.Range'? –

+3

Haben Sie tatsächlich Leistungsprobleme mit diesem Code? Ich kann mir nicht vorstellen, dass 100 Short Saiten sehr zeitraubend sein werden. Wenn Sie eine Methode verwenden, die die Schleife vor Ihnen versteckt, heißt das nicht, dass die Schleife nicht vorhanden ist. Es bedeutet lediglich, dass Sie den Code nicht schreiben müssen, um dies zu tun, aber Sie haben bereits den Code dafür geschrieben Ich bekomme nichts. – Servy

+0

es gibt selten etwas leistungsfähiger als die eingebauten C# -Funktionen ... aber die erste Frage, die Sie sich stellen müssen, ist, müssen Sie es leistungsfähiger machen? Haben Sie bei der Verwendung dieser Funktion eine Verschlechterung festgestellt ?! –

Antwort

1

wirklich um sich schnell müssen Sie (fast) alle Zuweisungen loszuwerden. Die meisten Leute wissen nicht, dass i.ToString() eine neue String-Instanz zuweist, die nicht mehr benötigt wird, nachdem sie an die StringBuilder Instanz angehängt wurde.

Ihre ursprüngliche Version concat die "temp" Zeichenfolge mit jeder i.ToString(), was zu einer Zeichenfolge, die allmählich größer und größer und damit langsamer, jedes Mal eine neue etwas größere Zeichenfolge zuweisen. Das ist kein Problem für ein paar Strings, aber wenn Sie auf diese Weise Tausende oder Millionen von Strings schreiben, sieht es so aus, als würde diese Methode niemals enden.

Ein viel besserer Ansatz ist die Verwendung StringBuilder, die im Grunde eine veränderbare Zeichenfolge ist, die den vorhandenen Puffer erweitern kann. Dies ermöglicht ein effizientes Anhängen von Zeichenfolgen, ohne dass neue temporäre Zeichenfolgen erstellt werden, da Zeichenfolgeninstanzen nicht veränderbar sind. Jetzt sind Sie schon ganz gut, Sie können es besser machen, indem Sie für alle erwarteten Ganzzahlen ein char-Array mit großem Speicherbereich zuweisen. Dann gehen Sie nicht i.ToString(), sondern i.ToCharArray() in den letzten Char-Puffer. Das geht über die StringBuilder Ansatz über den Faktor 2.

ArrayToStringSteakOverCooked  0.39s 0.40s 0.43s 
ArrayToStringKeithNicolasAlternate2 0.43s 0.44s 0.46s 
ArrayToStringAlois     0.17s 0.16s 0.16s 

Die Zahlen oben mit diesem Code erreicht wurden

int[] Integers = Enumerable.Range(-5, 5 * 1000 * 1000).ToArray(); 

void ArrayToStringAlois() 
{ 
    char[] buffer = new char[Integers.Length * 11]; // an integer can have 10 digits plus sign = 11 digits. This buffer is big enough for all possible numbers 
    int startIdx = 0; 
    for (int i = 0; i < Integers.Length; i++) 
    { 
     startIdx += ToCharArray(Integers[i], buffer, startIdx); 
    } 

    string lret = new string(buffer, 0, startIdx); 

    GC.KeepAlive(lret); 
} 

public static int ToCharArray(int value, char[] buffer, int bufferIndex) 
{ 
    if (value == 0) 
    { 
     buffer[bufferIndex] = '0'; 
     return 1; 
    } 
    int len = 1; 
    int upperBound = 0; 
    if (value < 0) 
    { 
     buffer[bufferIndex] = '-'; 
     len = 2; 
     upperBound = 1; 
    } 
    value = Math.Abs(value); 

    for (int rem = value/10; rem > 0; rem /= 10) 
    { 
     len++; 
    } 

    for (int i = len - 1; i >= upperBound; i--) 
    { 
     buffer[bufferIndex + i] = (char)('0' + (value % 10)); 
     value /= 10; 
    } 
    return len; 
} 

void ArrayToStringSteakOverCooked() 
{ 
    var numbers = new StringBuilder(); 
    var length = Integers.Length; 
    for (int i = 0; i < length; i++) 
    { 
     numbers.Append(i.ToString()); 
    } 

    var lret = numbers.ToString(); 
    GC.KeepAlive(lret); 
} 

void ArrayToStringKeithNicolasAlternate2() 
{ 
    var lret = string.Concat(Integers); 
    GC.KeepAlive(lret); 
} 
+0

ist die Schleife schneller als 'len + = Math.Floor (Math.Log10 (Math.Abs ​​(Wert)) + 1)' –

+0

@KeithNicholas: Ja springt von 0,16s (Schleife) auf 0,20s (Log10). len + = Math.Floor (Math.Log10 (Math.Abs ​​(Wert)) + 1) ist daher 25% langsamer. –

+0

interessant, wenn ich es in meinen Timing-Code und erhöhen Sie die Größe auf 10 Millionen Elemente String Buffer und dies herauskommen in etwa gleich sein, sie tauschen zwischen schneller und langsamer –

2

Verwenden Sie StringBuilder, wenn Sie die Zeichenfolge viele Male verketten möchten. Verwenden Sie stattdessen array.Length.

static void Main(string[] args) 
{ 
    int[] array = FillArray(); //Imagine FillArray() Fills the array with numbers from 0 to 100 
    var sb = new StringBuilder(); 
    var len = array.Length; 
    for(int i = 0; i < len; i++) 
    { 
     sb.Append(i.ToString()); 
    }  
    var numbers = sb.ToString();   
} 
+1

Ist die Länge keine Eigenschaft? –

+0

Obwohl der Vorschlag, StringBuilder zu verwenden, korrekt ist, kann ich nicht sagen, dass es eine gute Antwort ist, weil es dem OP sagt, was, aber nicht warum. Aus der Perspektive eines Lernenden sieht es so aus, als würden Sie willkürliche Regeln deklarieren. –

+0

StringBuilder ist nett, aber Sie können immer noch mehr als 2 Mal schneller ohne StringBuilder. Siehe meine Antwort. –

0

Es gibt verschiedene Möglichkeiten, um eine bessere Leistung zu erzielen. In meinem Test String hat möglicherweise den leichten Vorsprung (Timing ist variabel über mehrere Durchläufe und die Zahlen sind ganz in der Nähe, über 10 Iterationen String immer am schnellsten war, hielt die beiden anderen auf dem Vertauschen herum schneller)

static void Orginal(int[] array) 
     {    
      string numbers = ""; 
      for (int i = 0; i < array.Count(); i++) 
      { 
       numbers += i.ToString(); 
      } 
     } 

     static void Improved(int[] array) 
     { 
      var numbers = new StringBuilder(); 
      for (int i = 0; i < array.Length; i++) 
      { 
       numbers.Append(i); 
      } 
     } 

     static void Alternate(int[] array) 
     { 
      var strings = array.Select(n => n.ToString()).ToArray(); 
      var s = string.Join("", strings); 
     } 
     static void Alternate2(int[] array) 
     { 
      string.Concat(array); 
     } 
     static void Time(string name, Action<int[]> action, int[] array) 
     { 
      Stopwatch stopwatch = Stopwatch.StartNew(); 
      action(array); 
      stopwatch.Stop(); 
      Console.WriteLine("{0} - {1}", name, stopwatch.ElapsedMilliseconds); 
     } 

     static void Main(string[] args) 
     { 
      var array = Enumerable.Range(0, 100000).ToArray(); 
      for (int i = 0; i < 10; i++) 
      { 
      Time("Original", Orginal, array); 
      Time("Improved", Improved, array); 
      Time("Alternate", Alternate, array); 
      Time("Alternate2", Alternate2, array); 
      } 

     } 

enter image description here

1

Versuchen string.Concat, die speziell dafür ausgelegt ist:

static void Main(string[] args) { 
    string numbers = string.Concat(FillArray()); 
    ... 
} 
+0

StringBuilder scheint schneller .... nur :) –

+0

@KeithNicholas Warum würden Sie denken, ein String Builder wäre schneller? Es ist wahrscheinlich gleich oder langsamer. – Servy

+0

Concat ist auf meiner Maschine ein wenig langsamer. Aber Sie können immer noch einen Faktor 2 schneller sein, wenn Sie StringBuilder überhaupt nicht verwenden. Siehe meine Antwort. –