Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Conversation

@stephentoub
Copy link
Member

Improves the throughput of the null/"c"/"t"/"T" default format for TimeSpan.ToString/TryFormat, porting over the approach/specialization from Utf8Formatter.

Contributes to https://github.com/dotnet/corefx/issues/30612
cc: @jkotas, @danmosemsft, @ahsonkhan

Benchmark:

using System;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Attributes.Jobs;
using BenchmarkDotNet.Running;

[MemoryDiagnoser]
[InProcess]
public class Benchmark
{
    private static void Main() => BenchmarkRunner.Run<Benchmark>();

    private TimeSpan _simple = new TimeSpan(1, 2, 3);
    private TimeSpan _maxValue = new TimeSpan(long.MaxValue);
    private char[] _tmp = new char[100];

    [Benchmark] public string SimpleToString() => _simple.ToString();
    [Benchmark] public bool SimpleTryFormat() => _simple.TryFormat(_tmp, out int charsWritten);

    [Benchmark] public string MaxToString() => _maxValue.ToString();
    [Benchmark] public bool MaxTryFormat() => _maxValue.TryFormat(_tmp, out int charsWritten);
}

Before/After:

Benchmark Before (ns) After (ns) Improvement
SimpleToString 126.8 44.76 2.83x
SimpleTryFormat 147.2 30.25 4.87x
MaxToString 391.9 111.56 3.51x
MaxTryFormat 282.7 106.23 2.66x

{
charsWritten = sb.Length;
sb.CopyTo(0, destination, sb.Length);
charsWritten = sb.Length;

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am curious, why re-order these lines?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No great reason, just that since it's trivial to do either way, seemed better to only write the number of chars once they'd actually already been written. It doesn't really matter.

}

// Standard formats
// Standard formats other than 'c'/'t'/'T', which should have already been handled.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Debug.Assert?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We throw below. That seemed sufficient.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. Makes sense.

{
int effectiveDigits = literal.ff;
while (effectiveDigits > 0)
while (effectiveDigits > 0 && fraction % 10 == 0)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use mod here and division within the loop. Would using DivRem be faster?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would using DivRem be faster?

It would require measuring. I didn't do any substantive changes to this G-formatting code, though, only minor cleanup.

{
requiredOutputLength = 9; // requiredOutputLength + 1 for the leading '-' sign
ticks = -ticks;
if (ticks < 0)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the second if (ticks < 0) is always false, isn't it?

if ticks is less than a zero then ticks = -ticks make it more than zero.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the second if (ticks < 0) is always false, isn't it?

No. -long.MinValue == long.MinValue.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for an answer. Pretty interesting that the same applies to int.MinValue == -int.MinValue, but not to sbyte and short.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That is because all arithmetic operations on types smaller than int are automatically promoted to int beforehand, see here.

@dotnet dotnet deleted a comment from stephentoub Jul 18, 2018
@stephentoub stephentoub merged commit 965ad0c into dotnet:master Jul 18, 2018
@stephentoub stephentoub deleted the timespanformatc branch July 18, 2018 23:10
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants