|
Randor wrote: Can you run the String.IndexOf benchmark again using StringComparison.Ordinal and post the result?
Thanks, it is the solution:
Now .NET 6.0 is 2X times FASTER than .NET Framework 4.8
Starting tests for .NET Framework 3.5, wait...
Elapsed Time for [String.Replace]: 51,0830947 sec
Elapsed Time for [String.IndexOf]: 0,5451713 sec
Elapsed Time for [String.Contains]: 0,5424978 sec
Elapsed Time for [String.SubString]: 17,7345287 sec
Elapsed Time for [String.Remove]: 15,021131 sec
Elapsed Time for [.NET 3.5]: 84,9272387 sec
Starting tests for .NET Framework 4.8, wait...
Elapsed Time for [String.Replace]: 53,2363039 sec
Elapsed Time for [String.IndexOf]: 0,5760411 sec
Elapsed Time for [String.Contains]: 0,595356 sec
Elapsed Time for [String.SubString]: 14,8117435 sec
Elapsed Time for [String.Remove]: 11,2053769 sec
Elapsed Time for [.NET 4.8]: 80,4258634 sec
Starting tests for .NET 6.0, wait...
Elapsed Time for [String.Replace]: 18,5094685 sec
Elapsed Time for [String.IndexOf]: 0,3495122 sec
Elapsed Time for [String.Contains]: 0,32621 sec
Elapsed Time for [String.SubString]: 12,1062252 sec
Elapsed Time for [String.Remove]: 10,1427749 sec
Elapsed Time for [.NET 6.0]: 41,4367067 sec
|
|
|
|
|
Yep,
Nobody even noticed my post.
Thanks for running the benchmark, I suspected it would be faster.
|
|
|
|
|
It's advisable to use a real benchmarking framework when you want to benchmark operations.
|
|
|
|
|
Pete O'Hanlon wrote: It's advisable to use a real benchmarking framework
Not in this case, StopWatch is sufficient.
|
|
|
|
|
georani wrote: Not in this case, StopWatch is sufficient.
Benchmark.NET makes sure that code is warmed up properly, eliminating simple things like tiered compilation whose defaults may have changed between .NET versions, for example. It also makes it fairly trivial to compare different runtimes: it is, after all, the tool that Microsoft itself uses for comparison of runtimes (to consciously decide when performance regressions are acceptable).
|
|
|
|
|
Does it really matter in real world applications ? it never has in one's I've been involved in
Life should not be a journey to the grave with the intention of arriving safely in a pretty and well-preserved body, but rather to skid in broadside in a cloud of smoke, thoroughly used up, totally worn out, and loudly proclaiming “Wow! What a Ride!" - Hunter S Thompson - RIP
|
|
|
|
|
pkfox wrote: Does it really matter in real world applications ?
Yes, my application become unusable after porting it to .NET 6, it uses a lot of string.IndexOf functions.
|
|
|
|
|
|
They probably fixed it make it theoretically faster to find a random string inside another random string which makes it worse for most common usage.
Is it possible that they are creating more objects with the new library to enable simpler garbage collection across multiple/asynchronous threads? There might be different design goals at play.
There was an interesting article I read years ago about some of the algorithms used for string functions in the early BASIC releases.
|
|
|
|
|
You didn't / should be using / comparing StringBuilder.
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
|
That doesn't mean you exclude it.
For that matter, if the string is already loaded in a StringBuilder, then use (SB instance).ToString().IndexOf()
"Before entering on an understanding, I have meditated for a long time, and have foreseen what might happen. It is not genius which reveals to me suddenly, secretly, what I have to say or to do in a circumstance unexpected by other people; it is reflection, it is meditation." - Napoleon I
|
|
|
|
|
|
Eddy Vluggen wrote: Note the #1 in all tests.
Thank you. I will test it it.
And C Language seems to be a great choice. Or Golang. Or Rust.
|
|
|
|
|
C because it close to assembly. Golang or rust aren't competing there.
If it is a core function of what you do, then it'd make sense; eliminate the dependency.
Bastard Programmer from Hell
"If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
|
|
|
|
|
Machine code generated from Rust usually levels with C in terms of raw performance, often outperforming it
|
|
|
|
|
Sure.
Bastard Programmer from Hell
"If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
|
|
|
|
|
I'd be interested to learn why "rust isn't competing here".
|
|
|
|
|
It's a LLVM.
Running in a fictional machine.
On top of a real machine.
Bastard Programmer from Hell
"If you just follow the bacon Eddy, wherever it leads you, then you won't have to think about politics." -- Some Bell.
|
|
|
|
|
LLVM is not a VM, but a (native) language compiler.
Rust compiles to native code just as C and C++ do, and it uses LLVM as the compiler back-end.
Rust will frequently be faster that C or C++ for implementing the same algorithm, and sometimes a bit slower.
|
|
|
|
|
You got a really weird number at String.IndexOf. Here are my results:
Elapsed Time for [String.Replace]: 16,8695848 sec
Elapsed Time for [String.IndexOf]: 2,0153508 sec
Elapsed Time for [String.SubString]: 11,7445885 sec
Elapsed Time for [String.Remove]: 10,1579051 sec
.NET 6 is 1.7x up to 2.9x faster.
I think these numbers are pretty consistent.
|
|
|
|
|
Prior to .NET 5, culture-specific comparisons used NLS[^] on Windows. Since .NET 5, they switched to using ICU[^] instead.
Globalization and ICU | Microsoft Docs[^]
There is a config switch to force .NET to use NLS instead, but it's not recommended:
Breaking change with string.IndexOf(string) from .NET Core 3.0 -> .NET 5.0[^]
If you change your IndexOf call to specify StringComparison.Ordinal or StringComparison.OrdinalIgnoreCase , the .NET 6 code is roughly 1.5x faster than the .NET Framework 4.8 equivalent.
"These people looked deep within my soul and assigned me a number based on the order in which I joined."
- Homer
|
|
|
|
|
likly this. core 3.1 default basic just fine, and then 5.1 is playing a different sport.
This sounds like same issue with defaults switching from Newtonsoft JSON to System.Text that Text uses case sensitive name matching
[Replace]: [IndexOf]: [SubString]: [Remove]:
core 3.1 run 1 21.7278133 sec 1.7657007 sec 10.897253 sec 7.2913159 sec
core 3.1 run 2 17.5640173 sec 1.4541324 sec 8.3179752 sec 6.0721578 sec
core 5.0 run 1 14.7799258 sec 20.315649 sec 7.6012973 sec 5.9379139 sec
core 5.0 run 2 15.2000948 sec 26.041709 sec 10.142171 sec 7.3596202 sec
core 6.0 run 1 10.7217398 sec 18.049464 sec 7.5585288 sec 7.5387282 sec
core 6.0 run 2 10.6481228 sec 17.215285 sec 7.5210471 sec 6.4885804 sec
fw 4.8 run 1 33.0995089 sec 1.7132278 sec 9.5126196 sec 6.9524378 sec
fw 4.8 run 2 28.0019003 sec 1.4240000 sec 7.9734736 sec 5.6638693 sec
|
|
|
|
|
I can reproduce the nature (if not the exact numbers) of these results using BenchmarkDotNet and the same code being benchmarked (i.e. the loop bodies). I added a fifth benchmark for IndexOf with StringComparison.Ordinal .
// * Summary *
BenchmarkDotNet=v0.13.1, OS=Windows 10.0.18363.2274 (1909/November2019Update/19H2)
Intel Core i9-9980HK CPU 2.40GHz, 1 CPU, 16 logical and 8 physical cores
.NET SDK=6.0.202
[Host] : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT
.NET 6.0 : .NET 6.0.4 (6.0.422.16404), X64 RyuJIT
.NET Framework 4.8 : .NET Framework 4.8 (4.8.4510.0), X64 RyuJIT
| Method | Job | Runtime | Mean | Error | StdDev | Ratio | RatioSD |
|--------------- |------------------- |------------------- |-------------:|-----------:|-----------:|------:|--------:|
| Replace | .NET 6.0 | .NET 6.0 | 1,350.99 ns | 7.502 ns | 7.017 ns | 0.33 | 0.01 |
| Replace | .NET Framework 4.8 | .NET Framework 4.8 | 4,150.69 ns | 80.004 ns | 82.158 ns | 1.00 | 0.00 |
| | | | | | | | |
| IndexOf | .NET 6.0 | .NET 6.0 | 22,561.81 ns | 435.204 ns | 446.923 ns | 10.44 | 0.34 |
| IndexOf | .NET Framework 4.8 | .NET Framework 4.8 | 2,189.63 ns | 43.739 ns | 88.354 ns | 1.00 | 0.00 |
| | | | | | | | |
| IndexOfOrdinal | .NET 6.0 | .NET 6.0 | 261.22 ns | 2.760 ns | 2.446 ns | 0.65 | 0.02 |
| IndexOfOrdinal | .NET Framework 4.8 | .NET Framework 4.8 | 403.50 ns | 7.982 ns | 9.502 ns | 1.00 | 0.00 |
| | | | | | | | |
| Substring | .NET 6.0 | .NET 6.0 | 12.12 ns | 0.310 ns | 0.413 ns | 0.93 | 0.05 |
| Substring | .NET Framework 4.8 | .NET Framework 4.8 | 13.07 ns | 0.326 ns | 0.563 ns | 1.00 | 0.00 |
| | | | | | | | |
| Remove | .NET 6.0 | .NET 6.0 | 64.00 ns | 1.208 ns | 1.770 ns | 1.09 | 0.04 |
| Remove | .NET Framework 4.8 | .NET Framework 4.8 | 59.23 ns | 1.020 ns | 1.253 ns | 1.00 | 0.00 |
|
|
|
|
|