-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Avoid OverflowException
in Boolean.TryFormat
#108572
Conversation
This may get superseded by #108575 |
@EgorBot -intel -arm64 -profiler using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
public class Benchmarks
{
static bool[] _values = Enumerable.Range(0, 256).Select(i => i % 2 == 0).ToArray();
static char[] _output = new char[4096];
[Benchmark]
public void WriteBools()
{
Span<char> output = _output;
foreach (var value in _values)
{
if (value.TryFormat(output, out int written))
output = output.Slice(written);
else
throw new InvalidOperationException();
}
Consume(output);
}
[MethodImpl(MethodImplOptions.NoInlining)]
void Consume(Span<char> _){}
} |
bool formatting is very unlikely to be on a hot path in scenarios targeted by Mono. I think it would be acceptable to take regression for Mono to keep the code simple and safe. |
Related: #77398 |
@EgorBot -mono -intel -arm64 using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
public class Benchmarks
{
static bool[] _values = Enumerable.Range(0, 256).Select(i => i % 2 == 0).ToArray();
static char[] _output = new char[4096];
[Benchmark]
public void WriteBools()
{
Span<char> output = _output;
foreach (var value in _values)
{
if (value.TryFormat(output, out int written))
output = output.Slice(written);
else
throw new InvalidOperationException();
}
Consume(output);
}
[MethodImpl(MethodImplOptions.NoInlining)]
void Consume(Span<char> _){}
} |
Agree. In fact it improves Mono-jit (mini), but slightly regresses Interpreter. |
@EgorBot -mono -intel -arm64 --envvars MONO_ENV_OPTIONS:--interpreter using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
public class Benchmarks
{
static bool[] _values = Enumerable.Range(0, 256).Select(i => i % 2 == 0).ToArray();
static char[] _output = new char[4096];
[Benchmark]
public void WriteBools()
{
Span<char> output = _output;
foreach (var value in _values)
{
if (value.TryFormat(output, out int written))
output = output.Slice(written);
else
throw new InvalidOperationException();
}
Consume(output);
}
[MethodImpl(MethodImplOptions.NoInlining)]
void Consume(Span<char> _){}
} |
@jkotas For monointerpreter, the ratio between #108572 and #108575 ratio is 0.60. Benchmark results on
|
Method | Toolchain | Mean | Error | Ratio |
---|---|---|---|---|
WriteBools | Main | 22.75 μs | 0.266 μs | 1.00 |
WriteBools | #108572 | 16.58 μs | 0.329 μs | 0.73 |
WriteBools | Main | 22.00 μs | 0.440 μs | 1.00 |
WriteBools | #108575 | 26.90 μs | 0.529 μs | 1.22 |
I think 20% regression is fine here, even the best case is still a lot slower than the JIT (16μs vs ~200ns) , so it can be handled via an intrinsic in the interpreter if it really becomes a bottle-neck. A safer/easier-to-read code worth it. |
208d0fb
to
d1f0cc0
Compare
`MemoryMarshal.AsBytes` would throw unnecessarily when `destination.Length` exceeds 0x3FFFFFFF.
d1f0cc0
to
917c458
Compare
@EgorBot -intel -arm64 -profiler using System.Runtime.CompilerServices;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Running;
BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args);
public class Benchmarks
{
static bool[] _values = Enumerable.Range(0, 256).Select(i => i % 2 == 0).ToArray();
static char[] _output = new char[4096];
[Benchmark]
public void WriteBools()
{
Span<char> output = _output;
foreach (var value in _values)
{
if (value.TryFormat(output, out int written))
output = output.Slice(written);
else
throw new InvalidOperationException();
}
Consume(output);
}
[MethodImpl(MethodImplOptions.NoInlining)]
void Consume(Span<char> _){}
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, I think it's fine to leave constants instead of .Length to avoid making mono even more slower, but I don't have a strong opinion. I think it should be a good idea to fold ldstr+length
in Mono too (I once tried to do so in Mono via mono/mono#16898 but didn't finish)
Issue opened: #108857 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
MemoryMarshal.AsBytes
would throw unnecessarily whendestination.Length
exceeds 0x3FFFFFFF.