Il modo più veloce per rimuovere il primo carattere in una stringa


207

Supponiamo di avere la seguente stringa

string data= "/temp string";

Se vogliamo rimuovere il primo personaggio /possiamo farlo in molti modi come:

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

Ma davvero non so quale abbia l'algoritmo migliore e lo faccia più velocemente.
Ce n'è uno che è il migliore o tutti sono uguali?


Vuoi rimuovere comunque il primo personaggio o devi verificare che questo personaggio sia effettivamente un /?
SRKX,

5
TrimStartnon rimuoverà il primo carattere, rimuoverà i ncaratteri dall'inizio. Substringè il più veloce.
Jaroslav Jandek,

ho solo bisogno di rimuovere qualsiasi primo personaggio
Amr Badawy,

6
Se stai rimuovendo qualsiasi primo personaggio, TrimStart()è completamente fuori discussione.
BoltClock

@BoltClock: sì, è quello che ho detto (digitato).
Jaroslav Jandek,

Risposte:


147

La seconda opzione in realtà non è la stessa delle altre: se la stringa è "/// pippo" diventerà "pippo" anziché "// pippo".

La prima opzione ha bisogno di un po 'più di lavoro per capire rispetto alla terza: vedrei l' Substringopzione come la più comune e leggibile.

(Ovviamente ciascuno di essi come singola istruzione non farà nulla di utile - dovrai assegnare il risultato a una variabile, possibilmente datastessa.)

Non prenderei in considerazione le prestazioni qui a meno che non stia effettivamente diventando un problema per te - nel qual caso l'unico modo che sapresti sarebbe avere casi di test, quindi è facile eseguire quei casi di test per ogni opzione e confrontare i risultati. Mi aspetterei Substringprobabilmente di essere il più veloce qui, semplicemente perché Substringfinisce sempre per creare una stringa da un singolo blocco dell'input originale, mentre Removedeve almeno potenzialmente incollare insieme un blocco iniziale e un blocco finale.


36
Controllo ora chiamando ciascuno circa 90000000 e vado il seguente risultato: Rimuovi: 06.63 - TrimStart: 04.71 - sottostringa: 03.09 quindi dal risultato la sottostringa è la migliore
Amr Badawy

5
Ricorda solo che quando esegui il test delle prestazioni in questo modo, sei interessato dalla memorizzazione nella cache della CPU, quindi devi farlo sulle stringhe casuali, che hai precompilato un array (elenco) con e selezionare casualmente l'elemento di tale array ( elenco).
ajeh,

12

So che questa è terra di iper-ottimizzazione, ma mi è sembrata una buona scusa per calciare le ruote BenchmarkDotNet. Il risultato di questo test (anche su .NET Core) è Substringleggermente più veloce di Remove, in questo test di esempio: 19.37ns contro 22.52ns per Remove. Quindi circa il 16% più veloce.

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>();
        }
    }
}

risultati:

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 |

9

Immagino questo Removee Substringlegherei per il primo posto, dal momento che entrambi assorbono una porzione della stringa di dimensioni fisse, mentre TrimStartesegue una scansione da sinistra con un test su ciascun personaggio e quindi deve eseguire esattamente lo stesso lavoro del altri due metodi. Seriamente, però, questo sta spaccando i capelli.


1
In realtà, Substringè più veloce di Remove, perché Removechiama Substring.
Jaroslav Jandek,

@Jaroslav: questo non è vero. Sia Substringe Removecontare su un metodo privato, FillSubstring.
Marcelo Cantos,

Non l'ho verificato, ma sembra molto plausibile:string Remove(this string source, int from, int to) { return source.SubString(0, from) + source.SubString(to); }
Dykam l'

1
@Jaroslav: sto fissando lo smontaggio di Reflector dei due metodi in mscorlib.dll in un ambiente di sviluppo Windows abbastanza convenzionale. Entrambi chiamano System.PInvoke.EE.AllocateStringper allocare l'oggetto stringa di destinazione e quindi chiamano FillSubstringper copiare i caratteri. Sto guardando la cosa sbagliata?
Marcelo Cantos,

1
@Marcelo: Comunque, il tuo primo commento originariamente diceva una cosa totalmente diversa. Probabilmente avrei dovuto usare una formulazione migliore, tuttavia il punto è valido ( Substring> Remove). Non commenterò ulteriormente perché la discussione ha impiegato abbastanza tempo.
Jaroslav Jandek,

6

Potresti profilarlo, se davvero ti interessasse. Scrivi un ciclo di molte iterazioni e guarda cosa succede. È possibile, tuttavia, che questo non sia il collo di bottiglia nell'applicazione e TrimStart sembra il più semanticamente corretto. Cerca di scrivere facilmente il codice prima di ottimizzare.


6
TrimStartè il meno corretto, dal momento che "//temp string".TrimStart('/')sarà non solo rimuovere la prima '/'.
Marcelo Cantos,

Quindi la funzione è scarsamente chiamata. Non sono un ragazzo C #.
Stefan Kendall,

@StefanKendall: guarda i tag
Vijay Singh Rana,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.