Fastest way to remove first char in a String

369,126

Solution 1

The second option really isn't the same as the others - if the string is "///foo" it will become "foo" instead of "//foo".

The first option needs a bit more work to understand than the third - I would view the Substring option as the most common and readable.

(Obviously each of them as an individual statement won't do anything useful - you'll need to assign the result to a variable, possibly data itself.)

I wouldn't take performance into consideration here unless it was actually becoming a problem for you - in which case the only way you'd know would be to have test cases, and then it's easy to just run those test cases for each option and compare the results. I'd expect Substring to probably be the fastest here, simply because Substring always ends up creating a string from a single chunk of the original input, whereas Remove has to at least potentially glue together a start chunk and an end chunk.

Solution 2

I know this is hyper-optimization land, but it seemed like a good excuse to kick the wheels of BenchmarkDotNet. The result of this test (on .NET Core even) is that Substring is ever so slightly faster than Remove, in this sample test: 19.37ns vs 22.52ns for Remove. So some ~16% faster.

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

Results:

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 |

Solution 3

I'd guess that Remove and Substring would tie for first place, since they both slurp up a fixed-size portion of the string, whereas TrimStart does a scan from the left with a test on each character and then has to perform exactly the same work as the other two methods. Seriously, though, this is splitting hairs.

Solution 4

You could profile it, if you really cared. Write a loop of many iterations and see what happens. Chances are, however, that this is not the bottleneck in your application, and TrimStart seems the most semantically correct. Strive to write code readably before optimizing.

Solution 5

In .Net Core also this works:

data = data[1..];
Share:
369,126

Related videos on Youtube

Amr Badawy
Author by

Amr Badawy

Senior Software Engineer Interesting in software design and architecture Linkedin | Twitter

Updated on February 22, 2022

Comments

  • Amr Badawy
    Amr Badawy over 2 years

    Say we have the following string

    string data= "/temp string";
    

    If we want to remove the first character / we can do by a lot of ways such as :

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

    But, really I don't know which one has the best algorithm and doing that faster..
    Is there a one that is the best or all are the same ?

    • SRKX
      SRKX almost 14 years
      Do you want to remove the first character anyway or do you need to check that this character is indeed a /?
    • Jaroslav Jandek
      Jaroslav Jandek almost 14 years
      TrimStart won't remove the first char, it will remove n chars from the beginning. Substring is the fastest.
    • Amr Badawy
      Amr Badawy almost 14 years
      i just need to remove any first character
    • BoltClock
      BoltClock almost 14 years
      If you're removing any first character, TrimStart() is completely out of the question.
    • Jaroslav Jandek
      Jaroslav Jandek almost 14 years
      @BoltClock: yeah, that's what I said (typed).
  • Alex S
    Alex S almost 14 years
    TrimStart is the least correct, since "//temp string".TrimStart('/') will not just remove the first '/'.
  • Jaroslav Jandek
    Jaroslav Jandek almost 14 years
    Actually, Substring is faster than Remove, because Remove calls Substring.
  • Alex S
    Alex S almost 14 years
    @Jaroslav: This is not true. Both Substring and Remove rely on a private method, FillSubstring.
  • Dykam
    Dykam almost 14 years
    Didn't verify it, but it very sounds plausible: string Remove(this string source, int from, int to) { return source.SubString(0, from) + source.SubString(to); }
  • Jaroslav Jandek
    Jaroslav Jandek almost 14 years
    @Marcelo: maybe in Singularity RDK, but not in .NET (any version). Substring uses unsafe string copy (in memory).
  • Alex S
    Alex S almost 14 years
    @Jaroslav: I'm staring at the Reflector disassembly of the two methods in mscorlib.dll on a fairly conventional Windows dev environment. They both call System.PInvoke.EE.AllocateString to allocate the destination string object and then call FillSubstring to copy characters across. Am I looking at the wrong thing?
  • Stefan Kendall
    Stefan Kendall almost 14 years
    The function is poorly named then. I'm not a C# guy.
  • Amr Badawy
    Amr Badawy almost 14 years
    I do check now by call each one about 90000000 and I go the following result : Remove : 06.63 - TrimStart : 04.71 - subString : 03.09 so from result substring is the best
  • Alex S
    Alex S almost 14 years
    @Jaroslav: That wasn't your original point at all. If A calls B then it is (almost) a truism that A will be slower than B. Your latest claim, however, relies on several assumptions, such as the assumption that the overhead of Remove's extra call to FillSubstring, which has no work to do, will outweigh the cost of Substring's more complex parameter verification code. Don't get me wrong. I'm not disagreeing with your conclusion. Substring probably is faster (a matter one would settle by profiling, not speculating). Just don't pretend that we've been debating the same point from the outset.
  • Jaroslav Jandek
    Jaroslav Jandek almost 14 years
    @Marcelo: Remove esentially calls substring (work-wise) ((A + B) > A always - it does the same checks btw.). And yes, it is WAY slower than Substring. Like 80%+ slower for this specific operation. If you wanted to do a regular Remove from the middle of a string, even then is Remove only marginally (10%+) faster than Substring. Also, I see no calls to FillSubstring in my methods. The bottom line is the SubString is the best method for that as I have pointed out in the first comment on the original post.
  • Jaroslav Jandek
    Jaroslav Jandek almost 14 years
    @Marcelo: Anyway, your first comment originally said a totaly different thing. I should probably have used a better wording, the point is valid though (Substring > Remove). I am not going to comment further because the discussion took enough of my time.
  • Vijay Singh Rana
    Vijay Singh Rana over 9 years
    @StefanKendall: Look at tags
  • ajeh
    ajeh almost 8 years
    Just remember that when you are testing performance this way, you are affected by CPU caching, so you need to do that on the random strings, that you pre-populated an array (list) with, and randomly select the element of that array (list).