Der schnellste Weg, um das erste Zeichen in einem String zu entfernen

206

Angenommen, wir haben die folgende Zeichenfolge

string data= "/temp string";

Wenn wir das erste Zeichen entfernen möchten, /können wir dies auf viele Arten tun:

data.Remove(0,1);
data.TrimStart('/');
data.Substring(1);

Aber wirklich, ich weiß nicht, welcher den besten Algorithmus hat und das schneller macht.
Gibt es einen, der der beste ist oder alle gleich sind?

Amr Badawy
quelle
Möchten Sie das erste Zeichen trotzdem entfernen oder müssen Sie überprüfen, ob dieses Zeichen tatsächlich ein Zeichen ist /?
SRKX
5
TrimStartDas erste nZeichen wird nicht entfernt, sondern die Zeichen von Anfang an. Substringist der schnellste.
Jaroslav Jandek
Ich muss nur jeden ersten Charakter entfernen
Amr Badawy
6
Wenn Sie ein erstes Zeichen entfernen, TrimStart()kommt dies nicht in Frage.
BoltClock
@ BoltClock: Ja, das habe ich gesagt (getippt).
Jaroslav Jandek

Antworten:

146

Die zweite Option ist wirklich nicht die gleiche wie die anderen - wenn die Zeichenfolge "/// foo" ist, wird sie zu "foo" anstelle von "// foo".

Die erste Option erfordert etwas mehr Arbeit als die dritte - ich würde die SubstringOption als die häufigste und lesbarste betrachten.

(Offensichtlich wird jede von ihnen als einzelne Anweisung nichts Nützliches bewirken - Sie müssen das Ergebnis einer Variablen zuweisen, möglicherweise dataselbst.)

Ich würde die Leistung hier nicht berücksichtigen, es sei denn, dies würde tatsächlich zu einem Problem für Sie werden. In diesem Fall würden Sie nur Testfälle haben, und dann ist es einfach, diese Testfälle für jede Option und auszuführen Vergleichen Sie die Ergebnisse. Ich würde erwarten Substring, hier wahrscheinlich der Schnellste zu sein, einfach weil Substringimmer eine Zeichenfolge aus einem einzelnen Block der ursprünglichen Eingabe erstellt wird, während Removezumindest potenziell ein Startblock und ein Endblock zusammengeklebt werden müssen.

Jon Skeet
quelle
35
Ich überprüfe jetzt, indem ich jeden ungefähr 90000000 anrufe und ich gehe das folgende Ergebnis: Entfernen: 06.63 - TrimStart: 04.71 - subString: 03.09, also vom Ergebnis Teilzeichenfolge ist das beste
Amr Badawy
5
Denken Sie daran, dass Sie beim Testen der Leistung auf diese Weise vom CPU-Caching betroffen sind. Daher müssen Sie dies für die zufälligen Zeichenfolgen tun, mit denen Sie ein Array (eine Liste) vorab gefüllt haben, und das Element dieses Arrays zufällig auswählen ( aufführen).
Ajeh
12

Ich weiß, dass dies ein Land der Hyperoptimierung ist, aber es schien eine gute Ausrede zu sein, die Räder zu treten BenchmarkDotNet. Das Ergebnis dieses Tests (sogar unter .NET Core) ist, Substringdass er etwas schneller ist als Removein diesem Beispieltest: 19,37 ns gegenüber 22,52 ns für Remove. Also ca. 16% schneller.

using System;
using BenchmarkDotNet.Attributes;

namespace BenchmarkFun
{
    public class StringSubstringVsRemove
    {
        public readonly string SampleString = " My name is Daffy Duck.";

        [Benchmark]
        public string StringSubstring() => SampleString.Substring(1);

        [Benchmark]
        public string StringRemove() => SampleString.Remove(0, 1);

        public void AssertTestIsValid()
        {
            string subsRes = StringSubstring();
            string remvRes = StringRemove();

            if (subsRes == null
                || subsRes.Length != SampleString.Length - 1
                || subsRes != remvRes) {
                throw new Exception("INVALID TEST!");
            }
        }
    }

    class Program
    {
        static void Main()
        {
            // let's make sure test results are really equal / valid
            new StringSubstringVsRemove().AssertTestIsValid();

            var summary = BenchmarkRunner.Run<StringSubstringVsRemove>();
        }
    }
}

Ergebnisse:

BenchmarkDotNet=v0.11.4, OS=Windows 10.0.17763.253 (1809/October2018Update/Redstone5)
Intel Core i7-6700HQ CPU 2.60GHz (Skylake), 1 CPU, 8 logical and 4 physical cores
.NET Core SDK=3.0.100-preview-010184
  [Host]     : .NET Core 3.0.0-preview-27324-5 (CoreCLR 4.6.27322.0, CoreFX 4.7.19.7311), 64bit RyuJIT
  DefaultJob : .NET Core 3.0.0-preview-27324-5 (CoreCLR 4.6.27322.0, CoreFX 4.7.19.7311), 64bit RyuJIT

|          Method |     Mean |     Error |    StdDev |
|---------------- |---------:|----------:|----------:|
| StringSubstring | 19.37 ns | 0.3940 ns | 0.3493 ns |
|    StringRemove | 22.52 ns | 0.4062 ns | 0.3601 ns |
Nicholas Petersen
quelle
9

Ich würde das erraten Removeund Substringwürde den ersten Platz belegen, da beide einen Teil der Zeichenfolge mit fester Größe schlürfen, während TrimStartein Scan von links mit einem Test für jedes Zeichen durchgeführt wird und dann genau die gleiche Arbeit wie die ausführen muss andere zwei Methoden. Im Ernst, das spaltet die Haare.

Marcelo Cantos
quelle
1
Eigentlich Substringist schneller als Remove, weil RemoveAnrufe Substring.
Jaroslav Jandek
@ Jaroslav: Das ist nicht wahr. Beides Substringund Removestützen sich auf eine private Methode FillSubstring.
Marcelo Cantos
string Remove(this string source, int from, int to) { return source.SubString(0, from) + source.SubString(to); }
Ich
1
@ Jaroslav: Ich starre auf die Reflektor-Disassemblierung der beiden Methoden in mscorlib.dll in einer ziemlich konventionellen Windows-Entwicklungsumgebung. Beide rufen System.PInvoke.EE.AllocateStringauf, um das Zielzeichenfolgenobjekt zuzuweisen, und rufen dann auf, FillSubstringum Zeichen zu kopieren. Schaue ich auf das Falsche?
Marcelo Cantos
1
@ Marcelo: Wie auch immer, dein erster Kommentar sagte ursprünglich etwas ganz anderes. Ich hätte wahrscheinlich einen besseren Wortlaut verwenden sollen, der Punkt ist jedoch gültig ( Substring> Remove). Ich werde nicht weiter darauf eingehen, da die Diskussion genug Zeit in Anspruch genommen hat.
Jaroslav Jandek
6

Sie könnten es profilieren, wenn Sie sich wirklich darum kümmern. Schreiben Sie eine Schleife mit vielen Iterationen und sehen Sie, was passiert. Es besteht jedoch die Möglichkeit, dass dies nicht der Engpass in Ihrer Anwendung ist und TrimStart semantisch korrekt erscheint. Versuchen Sie, den Code vor der Optimierung lesbar zu schreiben.

Stefan Kendall
quelle
6
TrimStartrichtig, da ist die am wenigsten "//temp string".TrimStart('/')wird nicht entfernen Sie einfach das erste '/'.
Marcelo Cantos
Die Funktion ist dann schlecht benannt. Ich bin kein C # -Typ.
Stefan Kendall
@StefanKendall: Schauen Sie sich Tags
Vijay Singh Rana