Thanks to visit codestin.com
Credit goes to github.com

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

Conversation

stephentoub
Copy link
Member

Per the discussion at https://github.com/dotnet/coreclr/issues/3439, this PR at least temporarily changes the implementation of Math.DivRem to avoid two idiv instructions. It then replaces a few % and / pairs in mscorlib with Math.DivRem.

Perf test:

using System;
using System.Diagnostics;

class Program
{
    static long div, rem, a, b;

    static void Main()
    {
        // Various sets of a/b inputs
        var pairs = new[]
        {
            Tuple.Create<long, long>(0, 1),
            Tuple.Create<long, long>(4, 2),
            Tuple.Create<long, long>(99, 10),
            Tuple.Create<long, long>(-99, -10),
            Tuple.Create<long, long>(-99, 10),
            Tuple.Create<long, long>(4, 12),
            Tuple.Create<long, long>(1, long.MaxValue),
            Tuple.Create<long, long>(long.MaxValue, 1),
            Tuple.Create<long, long>(long.MaxValue, 3),
            Tuple.Create<long, long>(long.MaxValue, long.MaxValue - 1),
        };
        const int Iters = 50000000;

        // Time the old and the new on each input
        var sw = new Stopwatch();
        foreach (Tuple<long, long> t in pairs)
        {
            a = t.Item1;
            b = t.Item2;

            sw.Restart();
            for (int i = 0; i < Iters; i++) div = DivRem_Old(a, b, out rem);
            sw.Stop();
            TimeSpan oldTime = sw.Elapsed;

            sw.Restart();
            for (int i = 0; i < Iters; i++) div = DivRem_New(a, b, out rem);
            sw.Stop();
            TimeSpan newTime = sw.Elapsed;

            Console.WriteLine($"Old: {oldTime} New: {newTime}");
        }
    }

    public static long DivRem_Old(long a, long b, out long result)
    {
        result = a % b;
        return a / b;
    }

    public static long DivRem_New(long a, long b, out long result)
    {
        long div = a / b;
        result = a - (div * b);
        return div;
    }
}

outputs for me:

> corerun divremtest.exe
Old: 00:00:01.0659336 New: 00:00:00.5490556
Old: 00:00:01.0559024 New: 00:00:00.5516266
Old: 00:00:01.0675624 New: 00:00:00.5485116
Old: 00:00:01.0537601 New: 00:00:00.5483118
Old: 00:00:01.0612407 New: 00:00:00.5526581
Old: 00:00:01.0874783 New: 00:00:00.5545691
Old: 00:00:01.1375759 New: 00:00:00.5528239
Old: 00:00:01.0875608 New: 00:00:00.5720249
Old: 00:00:01.1346930 New: 00:00:00.6171032
Old: 00:00:01.1621859 New: 00:00:00.5689636

cc: @jkotas, @mikedn, @redknightlois

Until the JIT is able to eliminate one of the two idiv operations, using a multiplication and subtraction is measurably faster than an extra division.
@redknightlois
Copy link

I would aggresively inline it by default if it were my codebase.

@mikedn
Copy link

mikedn commented Nov 15, 2016

I'm not sure that using DivRem when the divisor is constant is a good idea. The JIT already handles this partially and I have an open PR that will improve that.

@stephentoub
Copy link
Member Author

I'm not sure that using DivRem when the divisor is constant is a good idea

Reverting those

Went through all uses of % looking for places DivRem could be used.  This looks like it's the only one of note.
@mikedn
Copy link

mikedn commented Nov 15, 2016

LGTM

1 similar comment
@redknightlois
Copy link

LGTM

@stephentoub
Copy link
Member Author

@dotnet-bot test Ubuntu x64 Checked Build and Test please (https://github.com/dotnet/coreclr/issues/6574)

@stephentoub stephentoub merged commit 68e3e94 into dotnet:master Nov 15, 2016
@stephentoub stephentoub deleted the divrem_perf branch November 15, 2016 22:22
@karelz karelz modified the milestone: 2.0.0 Aug 28, 2017
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.

6 participants