Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.13.2" />
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.13.2" />
<PackageReference Include="BenchmarkDotNet" Version="0.15.8" />
<PackageReference Include="BenchmarkDotNet.Annotations" Version="0.15.8" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -1,145 +1,156 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes;
using System.Text.RegularExpressions;

namespace FirstLetterToUpper
namespace FirstLetterToUpper;

[MemoryDiagnoser]
[Orderer(BenchmarkDotNet.Order.SummaryOrderPolicy.FastestToSlowest)]
[RankColumn]
public partial class FirstLetterToUpperMethods
{
[MemoryDiagnoser]
[Orderer(BenchmarkDotNet.Order.SummaryOrderPolicy.FastestToSlowest)]
[RankColumn]
public class FirstLetterToUpperMethods
public IEnumerable<object[]> SampleStrings()
{
yield return new object[] { GenerateRandomString(2000)};
}

[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharSubstring(string input)
{
public IEnumerable<object[]> SampleStrings()
if (string.IsNullOrEmpty(input))
{
yield return new object[] { GenerateRandomString(2000)};
return string.Empty;
}

[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharSubstring(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return $"{input[0].ToString().ToUpper()}{input.Substring(1)}";
}

return $"{input[0].ToString().ToUpper()}{input.Substring(1)}";
[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToUpper(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}

[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToUpper(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return $"{char.ToUpper(input[0])}{input[1..]}";
}

return $"{char.ToUpper(input[0])}{input[1..]}";
[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToCharArray(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}

[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToCharArray(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
var stringArray = input.ToCharArray();

var stringArray = input.ToCharArray();
if (char.IsLower(stringArray[0]))
{
stringArray[0] = char.ToUpper(stringArray[0]);
}

if (char.IsLower(stringArray[0]))
{
stringArray[0] = char.ToUpper(stringArray[0]);
}
return new string(stringArray);
}

return new string(stringArray);
[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToUpperAsSpan(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}

[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToUpperAsSpan(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
Span<char> destination = stackalloc char[1];

Span<char> destination = stackalloc char[1];
input.AsSpan(0, 1).ToUpperInvariant(destination);

input.AsSpan(0, 1).ToUpperInvariant(destination);
return $"{destination}{input.AsSpan(1)}";
}

return $"{destination}{input.AsSpan(1)}";
}
[GeneratedRegex("^\\p{Ll}")]
private static partial Regex FirstLowerLetter();

[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToUpperRegex(string input)
[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToUpperRegex(string input)
{
if (string.IsNullOrEmpty(input))
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}

return Regex.Replace(input, "^[a-z]", c => c.Value.ToUpper());
return string.Empty;
}

[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToUpperLinq(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
return FirstLowerLetter().Replace(input, c => c.Value.ToUpper());
}

return $"{input.FirstOrDefault().ToString().ToUpper()}{input.Substring(1)}";
[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToUpperLinq(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}

return $"{input.FirstOrDefault().ToString().ToUpper()}{input.Substring(1)}";
}

[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToUpperStringCreate(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}

return string.Create(input.Length, input, static (Span<char> chars, string str) =>
{
chars[0] = char.ToUpperInvariant(str[0]);
str.AsSpan(1).CopyTo(chars[1..]);
});
[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToUpperStringCreate(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}

[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToUpperUnsafeCode(string input)
return string.Create(input.Length, input, static (Span<char> chars, string str) =>
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}
chars[0] = char.ToUpperInvariant(str[0]);
str.AsSpan(1).CopyTo(chars[1..]);
});
}

/// <summary>
/// Demonstration code only. This method does not build a new string: it pins the string it was
/// given and overwrites its first character in place, so every other variable, field or
/// dictionary key pointing at the same instance changes with it. Identical string literals in an
/// assembly share one interned instance, which makes the damage reach code this method never saw.
/// Do not use it in production.
/// </summary>
[Benchmark]
[ArgumentsSource(nameof(SampleStrings))]
public string FirstCharToUpperUnsafeCode(string input)
{
if (string.IsNullOrEmpty(input))
{
return string.Empty;
}

unsafe
unsafe
{
// Mutates the instance itself. If input is an interned literal, every reference to
// that literal now reads the modified value. See the XML comment above.
fixed (char* p = input)
{
fixed (char* p = input)
{
*p = char.ToUpper(*p);
}
*p = char.ToUpper(*p);
}

return input;
}

private string GenerateRandomString(int size)
{
var random = new Random();
return input;
}

var charOptions = "abcdefghijklmnopqrstuvwxyz";
private string GenerateRandomString(int size)
{
var random = new Random();

return new string(Enumerable.Repeat(charOptions, size).Select(s => s[random.Next(s.Length)]).ToArray()).ToLower();
}
var charOptions = "abcdefghijklmnopqrstuvwxyz";

return new string(Enumerable.Repeat(charOptions, size).Select(s => s[random.Next(s.Length)]).ToArray()).ToLower();
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.8" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
<PackageReference Include="coverlet.collector" Version="3.1.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.10.1" />
<PackageReference Include="MSTest.TestAdapter" Version="4.4.0" />
<PackageReference Include="MSTest.TestFramework" Version="4.4.0" />
<PackageReference Include="coverlet.collector" Version="10.0.1" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading
Loading