-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
base: main
Are you sure you want to change the base?
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> _){}
} |
MemoryMarshal.AsBytes
would throw unnecessarily whendestination.Length
exceeds 0x3FFFFFFF.