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

Conversation

@jamesqo
Copy link

@jamesqo jamesqo commented Mar 2, 2017

@jkotas
Copy link
Member

jkotas commented Mar 2, 2017

This branch has conflicts that must be resolved

@jamesqo
Copy link
Author

jamesqo commented Mar 2, 2017

@jkotas Yeah, that's because of the codeformatter changes that recently went through (somewhat glad to see those conflicts, actually). Just fixed them with GitHub editor.

@jamesqo
Copy link
Author

jamesqo commented Mar 2, 2017

I fixed the conflicts for real after verifying that it compiled on my machine.

Copy link
Member

@safern safern left a comment

Choose a reason for hiding this comment

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

Thanks @jamesqo, are you adding tests for this new API in corefx side right?

@jamesqo
Copy link
Author

jamesqo commented Mar 2, 2017

@safern Yes.

@danmoseley
Copy link
Member

When you have the corefx PR up we can go ahead and merge this one. That rule helps us not forget ..

@jamesqo
Copy link
Author

jamesqo commented Mar 3, 2017

@danmosemsft The issues are not closed until the API is fully exposed in corefx, but sure, I'll submit the corefx tests (which I'm currently working on) alongside this PR.

@danmoseley
Copy link
Member

@jamesqo I shouldn't have said forget -- the problem is if the corefx change never goes in, someone has to go do the work to rollback the coreclr change. You are completely reliable of course :), but that has happened recently with another contributor. So better to know the corefx change is ready to go.

@danmoseley
Copy link
Member

@jkotas do you have any concerns about any subtle bad effect this may accidentally have on codegen? given this is rather critical code.

@jkotas
Copy link
Member

jkotas commented Mar 3, 2017

When in doubts about perf ... measure!

using System;
using System.Collections.Generic;

class Test
{

    static void Main()
    {
        int start = Environment.TickCount;
        for (int i = 0; i < 1000000; i++)
        {
            var dict = new Dictionary<int, string>(1000);
            for (int j = 0; j < 500; j++)
                dict.Add(j, "Hello");
        }
        int end = Environment.TickCount;
        Console.WriteLine((end - start).ToString());
    }
}

Baseline (average of multiple runs): 9438ms
With changes (average of multiple runs): 9875ms

So there is about 5% regression for Dictionary<int, string>.Add method. This regression is unnecessary. It can be completely eliminated by structuring the change differently. Leaving the details as an exercise for the reader ;)

@jkotas jkotas added the * NO MERGE * The PR is not ready for merge yet (see discussion for detailed reasons) label Mar 3, 2017
@danmoseley
Copy link
Member

Should also check public TValue this[TKey key] perf isn't affected.

@jamesqo
Copy link
Author

jamesqo commented Mar 3, 2017

Baseline (average of multiple runs): 9438ms
With changes (average of multiple runs): 9875ms

@jkotas, 37 / 9438 is 0.3%, not 5%.

@stephentoub
Copy link
Member

37 / 9438

Read the numbers Jan shared again? 😄

@jamesqo
Copy link
Author

jamesqo commented Mar 3, 2017

@stephentoub Wow, that should have taken me less than 5 minutes of staring at the comment to figure that out. Will fix the implementation and run tests to make sure perf doesn't regress again.

@jamesqo
Copy link
Author

jamesqo commented Mar 4, 2017

@jkotas I removed the extra branch I had accidentally introduced in Add, and I modified TryInsert to take an enum. After making the changes, I ran your microbenchmark 10 times against the old/new implementations and averaged the times.

Add, before: 12545.4
Add, after: 11928

I also modified it to use the indexer per @danmosemsft's concerns.

Indexer, before: 12484.4
Indexer, after: 11679.6

Copy link
Member

Choose a reason for hiding this comment

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

Enum.IsDefined is too slow, even for a debug check in this method.

Copy link
Author

Choose a reason for hiding this comment

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

@jkotas OK. I can remove the assert then.

Copy link

Choose a reason for hiding this comment

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

switch? I wouldn't do that. If the C# and JIT compilers decide to generate a jump table based switch (and it looks like they do that unfortunately) both perf and code size will suffer.

Besides, the code ends up being rather convoluted. The None case breaks to return false while the OverwriteExisting case returns true directly. And ThrowOnExisting breaks but of course it doesn't really do that in reality.

Just use if

if (behavior == InsertionBehavior.ThrowOnExisting)
    ThrowHelper.ThrowAddingDuplicateWithKeyArgumentException(key); 
if (behavior == InsertionBehavior.None)
    return false;
Debug.Assert(behavior == InsertBehavior.OverwriteExisting); // if you want
entries[i].value = value; 
version++; 
return true; 

Copy link
Author

Choose a reason for hiding this comment

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

@mikedn Sure, thanks for the advice. I modified your example to check for OverwriteExisting first because that will be the most common branch.

@jamesqo
Copy link
Author

jamesqo commented Mar 6, 2017

@jkotas Does this PR need anything else to go forward?

@jkotas
Copy link
Member

jkotas commented Mar 6, 2017

Could you please resolve conflicts in dotnet/corefx#16642 ?

@jkotas jkotas removed the * NO MERGE * The PR is not ready for merge yet (see discussion for detailed reasons) label Mar 6, 2017
@danmoseley danmoseley merged commit aeef0d2 into dotnet:master Mar 6, 2017
@jamesqo jamesqo deleted the try.add branch March 6, 2017 21:49
@jamesqo
Copy link
Author

jamesqo commented Mar 6, 2017

@jkotas Should get around to it within next day or so. Am busy today.

jorive pushed a commit to guhuro/coreclr that referenced this pull request May 4, 2017
* Add Dictionary.TryAdd

* Fix the conflicts for real

* Remove branch from Add(TKey, TValue)

* Tweak naming

* Remove a slow assert

* PR feedback
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants