Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.
/ corefx Public archive

Replaced ContainsKey() Calls with TryAdd() Dictionary Calls #17201

Merged
merged 35 commits into from
Mar 21, 2017

Conversation

rionmonster
Copy link

@rionmonster rionmonster commented Mar 16, 2017

Replaced usage of the Dictionary.ContainsKey() method with the newer Dictionary.TryAdd() method (where applicable) for performance benefits per Issue #17034.

This involved replacing this pattern:

if(!dictionary.ContainsKey(key))
{
        dictionary.Add(key, value);
}

with:

if (dictionary.TryAdd(key, value))
{
        // Code
}

Current Progress (Source)

System.CodeDom [Unsupported]
- [ ] ...\src\System\CodeDom\CodeNamespaceImportCollection.cs(34)

System.ComponentModel.TypeConverter

  • ...\src\System\ComponentModel\ReflectTypeDescriptionProvider.cs(451)
  • ...\src\System\ComponentModel\ReflectTypeDescriptionProvider.cs(489)

System.Data.Common

  • ...\src\System\Data\DataView.cs(1333) [reverted]
  • ...\src\System\Data\Common\AdapterUtil.cs(1185)

System.Data.SqlClient [Unsupported]
- [ ] ...\src\System\Data\Common\AdapterUtil.cs(976)
- [ ] ...\src\System\Data\SqlClient\SNI\SNIMarsConnection.cs(241)

System.Diagnostics [Unsupported]
- [ ] ...\src\System\Diagnostics\Tracing\EventSource.cs(6272)

System.DirectoryServices.AccountManagement [Unsupported]
- [ ] ...\src\System\DirectoryServices\AccountManagement\AD\ADDNLinkedAttrSet.cs(326)
- [ ] ...\src\System\DirectoryServices\AccountManagement\AD\ADDNLinkedAttrSet.cs(343)
- [ ] ...\src\System\DirectoryServices\AccountManagement\AD\ADDNLinkedAttrSet.cs(505)
- [ ] ...\src\System\DirectoryServices\AccountManagement\AD\ADDNLinkedAttrSet.cs(523)
- [ ] ...\src\System\DirectoryServices\AccountManagement\AD\ADDNLinkedAttrSet.cs(682)
- [ ] ...\src\System\DirectoryServices\AccountManagement\AD\ADDNLinkedAttrSet.cs(810)
- [ ] ...\src\System\DirectoryServices\AccountManagement\AD\ADStoreCtx_Query.cs(1178)

System.IO.Compression [Unsupported]
- [ ] ...\src\System\IO\Compression\ZipArchive.cs(368)

System.Linq.Expressions

  • ...\src\System\Linq\Expressions\Compiler\BoundConstants.cs(87)
  • ...\src\System\Linq\Expressions\Interpreter\LightCompiler.cs(1636)
  • ...\src\System\Linq\Expressions\Interpreter\LightCompiler.cs(1690)

System.Net.NetworkInformation [Unsupported]
- [ ] ...\src\System\Net\NetworkInformation\NetworkAddressChange.Windows.cs(104)
- [ ] ...\src\System\Net\NetworkInformation\NetworkAddressChange.Windows.cs(225)

System.Private.DataContractSerialization

  • ...\src\System\Runtime\Serialization\DataContract.cs(2109)
  • ...\src\System\Runtime\Serialization\HybridObjectCache.cs(45)
  • ...\src\System\Runtime\Serialization\Json\JsonDataContract.cs(293)
  • ...\src\System\Runtime\Serialization\Json\JsonDataContract.cs(302)

System.Private.Xml [Unsupported]
- [ ] ...\src\System\Xml\Dom\DocumentSchemaValidator.cs(195)
- [ ] ...\src\System\Xml\Dom\DocumentSchemaValidator.cs(203)
- [ ] ...\src\System\Xml\Dom\XmlNodeReader.cs(1015)
- [ ] ...\src\System\Xml\Dom\XmlNodeReader.cs(1023)
- [ ] ...\src\System\Xml\Schema\DtdParser.cs(1437)
- [ ] ...\src\System\Xml\Schema\DtdParser.cs(1444)
- [ ] ...\src\System\Xml\Schema\DtdParserAsync.cs(1033)
- [ ] ...\src\System\Xml\Schema\DtdParserAsync.cs(1040)
- [ ] ...\src\System\Xml\Schema\DtdParserAsync.cs(1124)
- [ ] ...\src\System\Xml\Schema\SchemaCollectionCompiler.cs(292)
- [ ] ...\src\System\Xml\Schema\SchemaCollectionCompiler.cs(772)
- [ ] ...\src\System\Xml\Schema\SchemaCollectionCompiler.cs(779)
- [ ] ...\src\System\Xml\Schema\SchemaInfo.cs(356)
- [ ] ...\src\System\Xml\Schema\SchemaInfo.cs(364)
- [ ] ...\src\System\Xml\Schema\SchemaInfo.cs(371)
- [ ] ...\src\System\Xml\Schema\SchemaInfo.cs(378)
- [ ] ...\src\System\Xml\Schema\SchemaInfo.cs(385)
- [ ] ...\src\System\Xml\Schema\SchemaSetCompiler.cs(158)
- [ ] ...\src\System\Xml\Schema\SchemaSetCompiler.cs(842)
- [ ] ...\src\System\Xml\Schema\XdrBuilder.cs(1052)
- [ ] ...\src\System\Xml\Schema\XmlSchema.cs(210)
- [ ] ...\src\System\Xml\Serialization\Compilation.cs(304)
- [ ] ...\src\System\Xml\Xsl\IlGen\StaticDataManager.cs(30)
- [ ] ...\src\System\Xml\Xsl\Xslt\Compiler.cs(104)
- [ ] ...\src\System\Xml\Xsl\Xslt\Compiler.cs(172)
- [ ] ...\src\System\Xml\Xsl\XsltOld\XsltCompileContext.cs(274)
- [ ] ...\tests\XmlSchema\XmlSchemaValidatorApi\ExceptionVerifier.cs(174)
- [ ] ...\tests\Xslt\XslTransformApi\ExceptionVerifier.cs(174)

System.Private.Xml.Linq
- [ ] ...\src\System\Xml\Schema\XNodeValidator.cs(156)

System.Reflection.Metadata [Unsupported]
- [ ] ...\src\System\Reflection\Metadata\Internal\NamespaceCache.cs(406)

System.Runtime.Serialization

  • ...\src\System\Runtime\Serialization\SerializationObjectManager.cs(30)

System.Security.AccessControl [Unsupported]
- [ ] ...\src\System\Security\AccessControl\Privilege.cs(150)

System.ServiceProcess.ServiceController [Unsupported]
- [ ] ...\src\System\ServiceProcess\ServiceController.cs(274)
- [ ] ...\src\System\ServiceProcess\ServiceController.cs(280)

Current Progress (Tests)

Common

  • ...\tests\System\Collections\IDictionary.Generic.Tests.cs(769)
  • ...\tests\System\Collections\IDictionary.Generic.Tests.cs(800)
  • ...\tests\System\Collections\IDictionary.Generic.Tests.cs(836)

System.Collections

  • ...\tests\Performance\Perf.Dictionary.cs(23)

System.Data.SqlClient

  • ...\tests\ManualTests\SQL\Common\InternalConnectionWrapper.cs(160)

System.Linq.Expressions

  • ...\tests\ILReader\ILPrinter.cs(123)

System.Private.Xml.Linq

  • ...\tests\TreeManipulation\XAttributeEnumRemove.cs(248)
  • ...\tests\TreeManipulation\XNodeSequenceRemove.cs(738)
  • ...\tests\xNodeBuilder\XmlReaderDiff.cs(42)

…plicable)

Replaced `ContainsKey()` method calls with the new `TryAdd()` method
where applicable.
@@ -1330,9 +1330,9 @@ internal void MaintainDataView(ListChangedType changedType, DataRow row, bool tr
_addNewRow = null;
_addNewMoved = new ListChangedEventArgs(ListChangedType.ItemMoved, index, Count - 1);
}
else if (!_rowViewCache.ContainsKey(row))
else if (_rowViewCache.TryAdd(row, buffer ?? new DataRowView(this, row)))
Copy link
Member

Choose a reason for hiding this comment

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

This should be reverted: if buffer is null, we'll end up allocating a new DataRowView even if row is already in the _rowViewCache.

Copy link
Author

Choose a reason for hiding this comment

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

Done.

{
caseDict.Add(key, caseOffset);
// Offset was added
Copy link
Member

Choose a reason for hiding this comment

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

This can just be:

else
{
    caseDict.TryAdd(key, caseOffset);
}

Copy link
Author

Choose a reason for hiding this comment

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

Done.

{
_referencedObjectDictionary.Add(id, null);
// Id was added
}
Copy link
Member

Choose a reason for hiding this comment

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

Same as in the earlier example, this can just be:

else
{
    _referencedObjectDictionary.TryAdd(id, null);
}

Copy link
Author

Choose a reason for hiding this comment

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

Done.

{
knownDataContracts.Add(itemDataContract.StableName, itemDataContract);
}
knownDataContracts.TryAdd(itemDataContract.StableName, itemDataContract);
Copy link
Member

@danmoseley danmoseley Mar 16, 2017

Choose a reason for hiding this comment

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

I think we generally only follow close brace by a blank line, close brace, or an else. Although I don't see that in https://github.com/dotnet/corefx/blob/master/Documentation/coding-guidelines/coding-style.md

Copy link
Author

Choose a reason for hiding this comment

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

Done.

And if that's missing, we may want to consider adding it in there. :)

Reverted to earlier approach to avoid unnessary allocation of  a new
row.
Removed the `TryAdd()` call from within the conditional statements and
moved them to the body
Updated various unit tests to include the `TryAdd()` instead of the
previous `ContainsKey()` pattern. This focused primarily on test casts
that were not dictionary-related at all to help curb any potential
regression issues.
Replaced older `ContainsKey()` implementation with newer `TryAdd()`.
@rionmonster
Copy link
Author

As far as the 'Vertical netfx Build' not working goes, should we revert the changes that were introduced to the tests?

It appears that the current tooling in Visual Studio recognized and allowed the tests to run and build locally, however this particular build didn't recognize the TryAdd() method on a few of the tests, namely here:

error CS1061: 'Dictionary>' does not contain a definition for 'TryAdd' and no extension method 'TryAdd' accepting a first argument of type 'Dictionary>' could be found (are you missing a using directive or an assembly reference?) [D:\j\workspace\netfx_debug_p---88b09898\src\System.Private.Xml.Linq\tests\TreeManipulation\System.Xml.Linq.TreeManipulation.Tests.csproj]

and here, here, and here:

error CS1061: 'Dictionary>' does not contain a definition for 'TryAdd' and no extension method 'TryAdd' accepting a first argument of type 'Dictionary>' could be found (are you missing a using directive or an assembly reference?) [D:\j\workspace\netfx_debug_p---88b09898\src\System.Private.Xml.Linq\tests\TreeManipulation\System.Xml.Linq.TreeManipulation.Tests.csproj]

And it would seem based on the discussion within the issues area that the items which are unchecked are not going to support this new API at this time, so it's likely we can ignore those. So these changes should be implemented everywhere that currently supports them.

@stephentoub
Copy link
Member

should we revert the changes that were introduced to the tests?

No, the tests that use .NET Core-specific surface area just need to be in their own files that are then only included when building the tests for .NET Core, e.g.
https://github.com/rionmonster/corefx/blob/ca9aab926cfa33bf1496de250e64c663bc249379/src/System.Collections/tests/System.Collections.Tests.csproj#L12
So those tests just need to be separated out into their own file(s), which would then be added into this portion of the .csproj.

@tarekgh tarekgh added this to the Future milestone Mar 17, 2017
@rionmonster
Copy link
Author

@stephentoub

As far as the existing tests go, there aren't many that will need to be migrated. For instance the Perf.Dictionary test will actually use this new TryAdd() call within a static method that is then used within a majority of the tests in that file:

public static Dictionary<int, int> CreateDictionary(int size)
{
            Random rand = new Random(837322);
            Dictionary<int, int> dict = new Dictionary<int, int>();
            while (dict.Count < size)
            {
                int key = rand.Next(500000, int.MaxValue);
                if (!dict.ContainsKey(key))
                    dict.Add(key, 0);
            }
            return dict;
}

Should I keep the old implementation within this current file and simply create a new Perf.Dictionary.netcoreapp.Tests.cs file with the new implementation? Or would it be fine to just move this entire file over to use the newer one (and forget the previous approach entirely)?

Changed test to earlier implementation in to make way for .NET Core
specific tests using newer API.
Copied the existing dictionary performance tests (from
`Perf.Dictionary.cs`) and used the newer `TryAdd()` calls; Adjusted the
projects test file only include these when targeting .NET Core.
@tarekgh
Copy link
Member

tarekgh commented Mar 17, 2017

Maybe we can create extension method for netfx that does TryAdd so we don't have to fork the code that using TryAdd method.

Reverted previous usage of the newer `TryAdd()` method and created a
separate .NET Core specific file to to be conditionally included and
tested.
Reverted previous changes involving the `TryAdd()` method and created a
seperate file to test this functionality only for .NET Core.
Reverted previous changes to the normal tests and created a seperate
test targeting .NET Core with the newer `TryAdd()` usage.
Reverted previous changes to the normal tests and created a seperate
test targeting .NET Core with the newer `TryAdd()` usage.
@rionmonster
Copy link
Author

I just pushed up some changes shifted out the .NET Core specific tests into their own files. The API changes were generally involved in a single method that was used throughout the tests (i.e. CreateDictionary()) so I simply created another version of the same tests, one using the new API calls and the older versions remained the same.

…Collisions

Applied Compilation Condition for InternalConnectionWrapper to Avoid
Collisions (i.e. either build for .NET Core or use the other one for
non-Core builds)
Added a .NET Core compilation constraint for the .NET Core specific
ILPrinter class to avoid issues when building (i.e. only build .NET Core
or non-Core).
Added a .NET Core compilation constraint for the .NET Core specific
Performance Dictionary testing class to avoid issues when building (i.e.
only build .NET Core or non-Core).
dict.Add(key, value);
return true;
}
return false;
Copy link
Member

Choose a reason for hiding this comment

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

Nit, we try to have only blank line, 'else', or close curly, directly after close curly.

@@ -9,6 +9,7 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' " />
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' " />
<ItemGroup>
<Compile Include="$(CommonTestPath)\System\Collections\DictionaryExtensions.cs" />
Copy link
Member

Choose a reason for hiding this comment

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

as Dan mentioned please add the Link element for having VS show such files nicely in the project view.

@@ -10,6 +10,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'netstandard-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="$(CommonTestPath)\System\Collections\DictionaryExtensions.cs" />
Copy link
Member

Choose a reason for hiding this comment

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

ditto

@@ -14,6 +14,7 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Debug|AnyCPU'" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'uap-Release|AnyCPU'" />
<ItemGroup>
<Compile Include="$(CommonTestPath)\System\Collections\DictionaryExtensions.cs" />
Copy link
Member

Choose a reason for hiding this comment

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

[](start = 0, length = 3)

ditto

@@ -10,6 +10,7 @@
<DefineConstants>$(DefineConstants);MANAGED_SNI</DefineConstants>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(CommonTestPath)\System\Collections\DictionaryExtensions.cs" />
Copy link
Member

Choose a reason for hiding this comment

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

[](start = 1, length = 3)

ditto

@danmoseley
Copy link
Member

LGTM

@@ -280,6 +280,7 @@
</Compile>
</ItemGroup>
<ItemGroup Condition=" '$(IsInterpreting)' != 'true' ">
<Compile Include="$(CommonTestPath)\System\Collections\DictionaryExtensions.cs" />
Copy link
Member

Choose a reason for hiding this comment

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

[](start = 0, length = 1)

ditto

@@ -10,6 +10,7 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<ItemGroup>
<Compile Include="$(CommonTestPath)\System\Collections\DictionaryExtensions.cs" />
Copy link
Member

Choose a reason for hiding this comment

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

ditto

@@ -10,6 +10,7 @@
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
</PropertyGroup>
<ItemGroup>
<Compile Include="$(CommonTestPath)\System\Collections\DictionaryExtensions.cs" />
Copy link
Member

Choose a reason for hiding this comment

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

ditto

@tarekgh
Copy link
Member

tarekgh commented Mar 20, 2017

other than the minor comments, LGTM. thanks for getting this ready and clean :-)

Fixed Coding Style Issue of a missing blank line following the closing
gullwing brace for the if-statement.
Updated all of the previously added extension method `<Compile>`
sections to include a `<Link>` section to improve formatting.
@rionmonster
Copy link
Author

The recent commits should address all of the issues that were previously mentioned (i.e. code formatting within the extension method and the addition of the <Link> sections within all of the previously added references).

Let me know if anything else jumps out :)

@@ -69,6 +69,7 @@
<Compile Include="$(CommonTestPath)\System\ObjectCloner.cs">
<Link>Common\System\ObjectCloner.cs</Link>
</Compile>
<Compile Include="$(CommonTestPath)\System\Collections\DictionaryExtensions.cs" />
Copy link
Member

Choose a reason for hiding this comment

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

<Compile Include="$(CommonTestPath [](start = 2, length = 36)

you missed adding the link element here.

Added Missing <Link> Section for Dictionary Extensions
@rionmonster
Copy link
Author

Nice catch!

I had just gone over he previous comments and didn't see it mentioned. It should be good to go now.

@tarekgh
Copy link
Member

tarekgh commented Mar 21, 2017

LGTM. thanks again @rionmonster

we'll wait the green CI and then will merge it.

{
expectedAttrsForParent.Add(p, p.Attributes().Except(copyAllAttributes.Where(x => x != null)).Select(a => new ExpectedValue(true, a)).ToList());
expectedAttrsForParent.TryAdd(p, p.Attributes().Except(copyAllAttributes.Where(x => x != null)).Select(a => new ExpectedValue(true, a)).ToList());
Copy link
Member

Choose a reason for hiding this comment

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

This change needs to be reverted. It's adding a lot of expense even if the key is already in the dictionary.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, wait, this is just in a test. Nevermind, that's fine.

{
expectedNodesForParent.Add(p, p.Nodes().Except(toRemoveCopy.Where(x => x != null)).Select(a => new ExpectedValue(!(a is XText), a)).ProcessNodes().ToList());
expectedNodesForParent.TryAdd(p, p.Nodes().Except(toRemoveCopy.Where(x => x != null)).Select(a => new ExpectedValue(!(a is XText), a)).ProcessNodes().ToList());
Copy link
Member

Choose a reason for hiding this comment

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

Ditto; this needs to be reverted.

Copy link
Member

Choose a reason for hiding this comment

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

Nevermind; I see now this is a test rather than product source.

Copy link
Member

@stephentoub stephentoub left a comment

Choose a reason for hiding this comment

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

LGTM. Thanks!

@stephentoub stephentoub merged commit 3d4d803 into dotnet:master Mar 21, 2017
@danmoseley
Copy link
Member

@rionmonster (can't rmeember whether I mentioned this before) if you'd like to take on something else, these are the "most wanted" as they're in our 2.0 milestone, which will ship in not long:

https://github.com/dotnet/corefx/issues?q=is%3Aopen+is%3Aissue+label%3A%22up+for+grabs%22+milestone%3A2.0.0

Perhaps there's something there that's interesting to you. If so just ask to be assigned it.

@karelz karelz modified the milestones: Future, 2.0.0 Mar 31, 2017
@rionmonster rionmonster deleted the tryadd-replacements branch April 4, 2017 14:39
@rionmonster rionmonster restored the tryadd-replacements branch April 4, 2017 14:41
@rionmonster rionmonster deleted the tryadd-replacements branch April 4, 2017 14:41
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.

6 participants