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

Add support for immutable dictionaries #37710

Merged
merged 31 commits into from
May 31, 2019
Merged

Add support for immutable dictionaries #37710

merged 31 commits into from
May 31, 2019

Conversation

layomia
Copy link
Contributor

@layomia layomia commented May 16, 2019

Partially addresses https://github.com/dotnet/corefx/issues/36643.

  • ImmutableDictionary<TKey, TValue>
  • IImmutableDictionary<TKey, TValue>
  • ImmutableSortedDictionary<TKey, TValue>

@layomia layomia added this to the 3.0 milestone May 16, 2019
@layomia layomia self-assigned this May 16, 2019
@ahsonkhan
Copy link
Member

cc @rynowak

Copy link
Member

@rynowak rynowak left a comment

Choose a reason for hiding this comment

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

I tried to add comments where I had something valuable to say. I think most of the details are in areas of the serializer that I'm not totally familiar with.

@steveharter
Copy link
Member

@layomia was there a before\after benchmark performed? With a large feature like this we should verify there are no surprises. Thanks.

@layomia
Copy link
Contributor Author

layomia commented May 31, 2019

Some performance notes.

TLDR

  • There's no significant performance impact on hot path (de)serialization i.e. primitives, objects, and enumerables.
  • There's a little (~4.6% avg.) regression for deserializing Dictionary, IDictionary, and IReadOnlyDictionary. This is due to some necessary if-checks introduced in shared code-paths between non-immutable dicts and immutable dicts. Splitting their code-paths entirely might mitigate this, but is beyond the scope for preview 6. This will be targeted in preview 7.
  • We might be able to improve the performance of (de)serializing ImmutableDicitonary, IImmutableDictionary, and ImmutableSortedDictionay by using IL emit to generate high performance CreateRange methods.

Hot-path deserialization and serialization use cases (Primitives, enumerables, objects)

These benchmarks live in the perf repo.

Before this change

[IndexViewModel]

Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
DeserializeFromString 232.6 us 2.239 us 1.870 us 232.0 us 229.1 us 235.3 us 8.4746 0.9416 - 36.55 KB
DeserializeFromUtf8Bytes 230.5 us 4.789 us 4.480 us 230.5 us 224.6 us 241.7 us 5.4745 - - 23.65 KB
DeserializeFromStream 224.9 us 2.964 us 2.773 us 226.3 us 219.1 us 228.1 us 5.4005 - - 24.02 KB
Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
SerializeToString 337.4 us 3.357 us 2.803 us 337.0 us 332.1 us 342.2 us 5.6818 - - 27.95 KB
SerializeToUtf8Bytes 371.0 us 22.396 us 24.893 us 364.4 us 335.1 us 435.9 us 2.6596 - - 15.12 KB
SerializeToStream 363.0 us 29.353 us 32.626 us 348.4 us 330.1 us 446.6 us - - - 2.39 KB

[Location]

Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
DeserializeFromString 8.986 us 0.2033 us 0.1902 us 8.995 us 8.718 us 9.462 us 0.1527 - - 704 B
DeserializeFromUtf8Bytes 8.693 us 0.1040 us 0.0922 us 8.693 us 8.551 us 8.871 us 0.1037 - - 472 B
DeserializeFromStream 9.431 us 0.1114 us 0.1042 us 9.455 us 9.262 us 9.637 us 0.1847 - - 856 B
Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
SerializeToString 8.457 us 0.5550 us 0.5939 us 8.239 us 7.765 us 10.029 us 0.1210 - - 608 B
SerializeToUtf8Bytes 8.041 us 0.5792 us 0.6438 us 7.791 us 7.467 us 9.539 us 0.0927 - - 400 B
SerializeToStream 7.825 us 0.0720 us 0.0638 us 7.840 us 7.730 us 7.913 us 0.0630 - - 352 B

[LoginViewModel]

Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
DeserializeFromString 3.117 us 0.0579 us 0.0568 us 3.107 us 3.032 us 3.248 us 0.0624 - - 304 B
DeserializeFromUtf8Bytes 2.960 us 0.0483 us 0.0452 us 2.952 us 2.905 us 3.049 us 0.0351 - - 192 B
DeserializeFromStream 3.614 us 0.0625 us 0.0554 us 3.618 us 3.516 us 3.687 us 0.1298 - - 576 B
Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
SerializeToString 3.022 us 0.0409 us 0.0383 us 3.013 us 2.962 us 3.106 us 0.0839 - - 360 B
SerializeToUtf8Bytes 2.967 us 0.0438 us 0.0409 us 2.951 us 2.909 us 3.047 us 0.0587 - - 280 B
SerializeToStream 3.229 us 0.0956 us 0.1023 us 3.197 us 3.119 us 3.504 us 0.0714 - - 352 B

[MyEventsListerViewModel]

Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
DeserializeFromString 2.247 ms 0.0439 ms 0.0411 ms 2.227 ms 2.206 ms 2.356 ms 35.3982 8.8496 - 161.9 KB
DeserializeFromUtf8Bytes 2.227 ms 0.0183 ms 0.0163 ms 2.228 ms 2.204 ms 2.263 ms 18.0180 - - 82.3 KB
DeserializeFromStream 2.412 ms 0.0410 ms 0.0342 ms 2.403 ms 2.355 ms 2.489 ms 19.2308 - - 82.68 KB
Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
SerializeToString 2.272 ms 0.0345 ms 0.0323 ms 2.266 ms 2.209 ms 2.312 ms 71.4286 44.6429 44.6429 370.61 KB
SerializeToUtf8Bytes 2.235 ms 0.0247 ms 0.0231 ms 2.237 ms 2.192 ms 2.264 ms 62.5000 8.9286 - 291 KB
SerializeToStream 2.269 ms 0.0523 ms 0.0537 ms 2.248 ms 2.195 ms 2.396 ms 44.6429 - - 211.62 KB

After this change

[IndexViewModel]

Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
DeserializeFromString 234.2 us 2.904 us 2.717 us 234.0 us 229.8 us 240.1 us 8.3643 0.9294 - 36.59 KB
DeserializeFromUtf8Bytes 227.5 us 2.527 us 2.240 us 227.6 us 223.2 us 230.9 us 5.3763 - - 23.68 KB
DeserializeFromStream 236.7 us 2.255 us 2.110 us 237.2 us 232.0 us 239.6 us 5.6926 - - 24.06 KB
Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
SerializeToString 338.1 us 2.541 us 2.377 us 338.2 us 334.5 us 342.3 us 6.6489 - - 27.98 KB
SerializeToUtf8Bytes 332.3 us 1.488 us 1.319 us 332.4 us 328.8 us 334.4 us 2.6596 - - 15.15 KB
SerializeToStream 338.8 us 3.786 us 3.356 us 339.1 us 332.9 us 343.6 us - - - 2.43 KB

[Location]

Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
DeserializeFromString 9.367 us 0.1075 us 0.1005 us 9.368 us 9.166 us 9.521 us 0.1473 - - 704 B
DeserializeFromUtf8Bytes 9.036 us 0.0908 us 0.0850 us 9.012 us 8.923 us 9.218 us 0.1113 - - 472 B
DeserializeFromStream 9.783 us 0.1341 us 0.1189 us 9.788 us 9.610 us 10.036 us 0.1969 - - 864 B
Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
SerializeToString 7.622 us 0.0856 us 0.0801 us 7.625 us 7.502 us 7.810 us 0.1236 - - 608 B
SerializeToUtf8Bytes 7.712 us 0.1600 us 0.1843 us 7.664 us 7.457 us 8.120 us 0.0903 - - 400 B
SerializeToStream 7.688 us 0.0395 us 0.0350 us 7.689 us 7.642 us 7.755 us 0.0615 - - 360 B

[LoginViewModel]

Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
DeserializeFromString 3.190 us 0.0296 us 0.0262 us 3.187 us 3.120 us 3.232 us 0.0634 - - 304 B
DeserializeFromUtf8Bytes 3.075 us 0.0394 us 0.0369 us 3.069 us 2.982 us 3.132 us 0.0365 - - 192 B
DeserializeFromStream 3.619 us 0.0430 us 0.0381 us 3.628 us 3.555 us 3.694 us 0.1314 - - 584 B
Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
SerializeToString 3.012 us 0.0321 us 0.0300 us 3.009 us 2.962 us 3.061 us 0.0858 - - 360 B
SerializeToUtf8Bytes 3.002 us 0.0749 us 0.0832 us 2.985 us 2.897 us 3.192 us 0.0582 - - 280 B
SerializeToStream 3.346 us 0.1224 us 0.1409 us 3.314 us 3.161 us 3.581 us 0.0763 - - 360 B

[MyEventsListerViewModel]

Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
DeserializeFromString 2.312 ms 0.0355 ms 0.0349 ms 2.307 ms 2.263 ms 2.365 ms 36.6972 9.1743 - 161.9 KB
DeserializeFromUtf8Bytes 2.344 ms 0.0440 ms 0.0412 ms 2.346 ms 2.263 ms 2.407 ms 18.5185 - - 82.34 KB
DeserializeFromStream 2.521 ms 0.0320 ms 0.0299 ms 2.509 ms 2.484 ms 2.583 ms 20.2020 - - 82.72 KB
Method Mean Error StdDev Median Min Max Gen 0/1k Op Gen 1/1k Op Gen 2/1k Op Allocated Memory/Op
SerializeToString 2.255 ms 0.0179 ms 0.0167 ms 2.254 ms 2.229 ms 2.281 ms 71.4286 44.6429 44.6429 370.64 KB
SerializeToUtf8Bytes 2.209 ms 0.0206 ms 0.0193 ms 2.214 ms 2.182 ms 2.239 ms 62.5000 8.9286 - 291.07 KB
SerializeToStream 2.246 ms 0.0507 ms 0.0449 ms 2.245 ms 2.180 ms 2.350 ms 44.6429 - - 211.66 KB

Deserialization and serialization of Dictionary, IDictionary, IReadOnlyDictionary

We care about the perf changes here because this PR modifies their code paths. The benchmarks are being added to the performance repo: dotnet/performance#530.

Before this change

Method Mean Error StdDev Median Gen 0 Gen 1 Gen 2 Allocated
DeserializeDict 698.0 ns 13.884 ns 22.811 ns 685.8 ns 0.0992 - - 416 B
DeserializeIDict 689.9 ns 6.155 ns 4.805 ns 690.4 ns 0.0992 - - 416 B
DeserializeIReadOnlyDict 689.1 ns 6.550 ns 5.470 ns 689.1 ns 0.0992 - - 416 B
SerializeDict 824.9 ns 18.945 ns 18.607 ns 821.0 ns 0.0954 - - 400 B
SerializeIDict 790.2 ns 15.665 ns 19.812 ns 784.8 ns 0.0954 - - 400 B
SerializeIReadOnlyDict 826.1 ns 19.380 ns 28.406 ns 820.8 ns 0.0954 - - 400 B

After this change

Method Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated
DeserializeDict 731.5 ns 14.684 ns 21.524 ns 0.0992 - - 416 B
DeserializeIDict 719.8 ns 14.314 ns 20.066 ns 0.0992 - - 416 B
DeserializeIReadOnlyDict 721.6 ns 14.251 ns 24.582 ns 0.0992 - - 416 B
SerializeDict 817.5 ns 16.344 ns 23.957 ns 0.0954 - - 400 B
SerializeIDict 802.7 ns 9.360 ns 8.755 ns 0.0954 - - 400 B
SerializeIReadOnlyDict 839.9 ns 16.863 ns 26.253 ns 0.0954 - - 400 B

[Added by this PR] Deserialization and serialization of ImmutableDictionary, IImmutableDictionary, ImmutableSortedDictionary

The benchmarks are being added to the performance repo: dotnet/performance#530.

Method Mean Error StdDev Median Gen 0 Gen 1 Gen 2 Allocated
DeserializeImmutableDict 1.947 us 0.0700 us 0.1974 us 1.876 us 0.6332 - - 2649 B
DeserializeIImmutableDict 1.949 us 0.0404 us 0.1147 us 1.895 us 0.6332 - - 2649 B
DeserializeImmutableSortedDict 2.532 us 0.0504 us 0.1283 us 2.527 us 0.7591 - - 3177 B
SerializeImmutableDict_ToBytes 1.606 us 0.0081 us 0.0071 us 1.606 us 0.1793 - - 752 B
SerializeIImmutableDict_ToBytes 1.685 us 0.0338 us 0.0473 us 1.677 us 0.1793 - - 752 B
SerializeImmutableSortedDict_ToBytes 1.501 us 0.0299 us 0.0265 us 1.507 us 0.1564 - - 656 B
SerializeImmutableDict_ToString 1.721 us 0.0271 us 0.0290 us 1.711 us 0.1869 - - 784 B
SerializeIImmutableDict_ToString 1.709 us 0.0234 us 0.0182 us 1.707 us 0.1869 - - 784 B
SerializeImmutableSortedDict_ToString 1.357 us 0.0112 us 0.0094 us 1.358 us 0.1640 - - 688 B

Json.NET perf

Method Mean Error StdDev Gen 0 Gen 1 Gen 2 Allocated
DeserializeDict 948.3 ns 5.016 ns 4.447 ns 0.7010 - - 2.87 KB
DeserializeIDict 959.0 ns 6.703 ns 5.942 ns 0.7000 - - 2.87 KB
DeserializeIReadOnlyDict 1,010.8 ns 2.997 ns 2.804 ns 0.7172 - - 2.94 KB
SerializeDict_ToString 593.5 ns 1.524 ns 1.351 ns 0.3443 - - 1.41 KB
SerializeIDict_ToString 607.2 ns 2.617 ns 2.448 ns 0.3443 - - 1.41 KB
SerializeIReadOnlyDict_ToString 598.5 ns 2.760 ns 2.581 ns 0.3443 - - 1.41 KB
DeserializeImmutableDict 1,426.5 ns 6.333 ns 5.924 ns 0.7629 - - 3.12 KB
DeserializeIImmutableDict 1,839.7 ns 17.861 ns 14.915 ns 0.8793 - - 3.59 KB
DeserializeImmutableSortedDict 1,726.2 ns 7.363 ns 6.527 ns 0.8793 - - 3.59 KB
SerializeImmutableDict_ToString 1,196.9 ns 6.363 ns 5.640 ns 0.3967 - - 1.63 KB
SerializeIImmutableDict_ToString 1,192.0 ns 3.545 ns 3.316 ns 0.3967 - - 1.63 KB
SerializeImmutableSortedDict_ToString 948.9 ns 4.334 ns 3.842 ns 0.3748 - - 1.53 KB
[JsonExporterAttribute.Full]
[MemoryDiagnoser]
public class JsonDotNet_DictionaryPerf
{
    private const string _jsonString = @"{""Hello"":""World"",""Hello2"":""World2""}";

    private Dictionary<string, string> _dict = new Dictionary<string, string>() { { "Hello", "World" }, { "Hello2", "World2" } };
    private static IDictionary<string, string> _iDict = new Dictionary<string, string>() { { "Hello", "World" }, { "Hello2", "World2" } };
    private IReadOnlyDictionary<string, string> _iReadOnlyDict = new Dictionary<string, string>() { { "Hello", "World" }, { "Hello2", "World2" } };

    private static ImmutableDictionary<string, string> _immutableDict;
    private static IImmutableDictionary<string, string> _iimmutableDict;
    private static ImmutableSortedDictionary<string, string> _immutableSortedDict;

    [GlobalSetup]
    public void Setup()
    {
        Dictionary<string, string> _dict = new Dictionary<string, string>() { { "Hello", "World" }, { "Hello2", "World2" } };

        _immutableDict = ImmutableDictionary.CreateRange(_dict);
        _iimmutableDict = ImmutableDictionary.CreateRange(_dict);
        _immutableSortedDict = ImmutableSortedDictionary.CreateRange(_dict);
    }

    [Benchmark]
    public Dictionary<string, string> DeserializeDict()
    {
        return JsonConvert.DeserializeObject<Dictionary<string, string>>(_jsonString);
    }

    [Benchmark]
    public IDictionary<string, string> DeserializeIDict()
    {
        return JsonConvert.DeserializeObject<IDictionary<string, string>>(_jsonString);
    }

    [Benchmark]
    public IReadOnlyDictionary<string, string> DeserializeIReadOnlyDict()
    {
        return JsonConvert.DeserializeObject<IReadOnlyDictionary<string, string>>(_jsonString);
    }

    [Benchmark]
    public string SerializeDict_ToString()
    {
        return JsonConvert.SerializeObject(_dict);
    }

    [Benchmark]
    public string SerializeIDict_ToString()
    {
        return JsonConvert.SerializeObject(_iDict);
    }

    [Benchmark]
    public string SerializeIReadOnlyDict_ToString()
    {
        return JsonConvert.SerializeObject(_iReadOnlyDict);
    }

    [Benchmark]
    public ImmutableDictionary<string, string> DeserializeImmutableDict()
    {
        return JsonConvert.DeserializeObject<ImmutableDictionary<string, string>>(_jsonString);
    }

    [Benchmark]
    public IImmutableDictionary<string, string> DeserializeIImmutableDict()
    {
        return JsonConvert.DeserializeObject<IImmutableDictionary<string, string>>(_jsonString);
    }

    [Benchmark]
    public ImmutableSortedDictionary<string, string> DeserializeImmutableSortedDict()
    {
        return JsonConvert.DeserializeObject<ImmutableSortedDictionary<string, string>>(_jsonString);
    }

    [Benchmark]
    public string SerializeImmutableDict_ToString()
    {
        return JsonConvert.SerializeObject(_immutableDict);
    }

    [Benchmark]
    public string SerializeIImmutableDict_ToString()
    {
        return JsonConvert.SerializeObject(_iimmutableDict);
    }

    [Benchmark]
    public string SerializeImmutableSortedDict_ToString()
    {
        return JsonConvert.SerializeObject(_immutableSortedDict);
    }
}

@ahsonkhan
Copy link
Member

ahsonkhan commented May 31, 2019

The failing CI legs are unrelated:

System.Text.RegularExpressions.Tests

https://mc.dot.net/#/user/dotnet-bot/pr~2Fdotnet~2Fcorefx~2Frefs~2Fpull~2F37710~2Fmerge/test~2Ffunctional~2Fcli~2Finnerloop~2F/20190531.49/workItem/System.Text.RegularExpressions.Tests/wilogs
```Executed on a0000O8 using docker image mcr.microsoft.com/dotnet-buildtools/prereqs:fedora-28-helix-09ca40b-20190508143249
running $HELIX_CORRELATION_PAYLOAD/scripts/0eba4f757465410aa2ec8b14c47f3505/execute.sh in /datadisks/disk1/work/b78a421c-0a73-4bdb-8065-48f4e99a8a61/Work/466cfcdc-d246-4d89-ae6d-a2d67b426cfe/Exec max 900 seconds

System.Net.Sockets.Tests - Windows.10.Amd64.ClientRS4.ES.Open-x64:Debug
https://mc.dot.net/#/user/dotnet-bot/pr~2Fdotnet~2Fcorefx~2Frefs~2Fpull~2F37710~2Fmerge/test~2Ffunctional~2Fcli~2Finnerloop~2F/20190531.49/workItem/System.Net.Sockets.Tests/wilogs

C:\dotnetbuild\work\24f15617-923a-4e2e-a2ad-b0ad65eb6de5\Work\94cffab9-8c8d-44ad-a838-77b03cdcfb47\Exec>"C:\dotnetbuild\work\24f15617-923a-4e2e-a2ad-b0ad65eb6de5\Payload\dotnet.exe" xunit.console.dll System.Net.Sockets.Tests.dll -xml testResults.xml -nologo -nocolor -notrait category=nonnetcoreapptests -notrait category=nonwindowstests -notrait category=IgnoreForCI -notrait category=OuterLoop -notrait category=failing  
  Discovering: System.Net.Sockets.Tests (method display = ClassAndMethod, method display options = None)
  Discovered:  System.Net.Sockets.Tests (found 707 of 1059 test cases)
  Starting:    System.Net.Sockets.Tests (parallel test collections = on, max threads = 4)
    System.Net.Sockets.Tests.KeepAliveTest.Socket_KeepAlive_RetryCount_Failure [SKIP]
      Condition(s) not met: "IsWindowsBelow1703"
    System.Net.Sockets.Tests.UnixDomainSocketTest.Socket_CreateUnixDomainSocket_Throws_OnWindows [SKIP]
      Condition(s) not met: "IsSubWindows10"
   System.Net.Sockets.Tests: [Long Running Test] 'System.Net.Sockets.Tests.UnixDomainSocketTest.Socket_SendReceiveAsync_PropagateToStream_Success', Elapsed: 00:02:15
   System.Net.Sockets.Tests: [Long Running Test] 'System.Net.Sockets.Tests.UnixDomainSocketTest.Socket_SendReceiveAsync_PropagateToStream_Success', Elapsed: 00:04:15
   System.Net.Sockets.Tests: [Long Running Test] 'System.Net.Sockets.Tests.UnixDomainSocketTest.Socket_SendReceiveAsync_PropagateToStream_Success', Elapsed: 00:06:15
   System.Net.Sockets.Tests: [Long Running Test] 'System.Net.Sockets.Tests.UnixDomainSocketTest.Socket_SendReceiveAsync_PropagateToStream_Success', Elapsed: 00:08:15
   System.Net.Sockets.Tests: [Long Running Test] 'System.Net.Sockets.Tests.UnixDomainSocketTest.Socket_SendReceiveAsync_PropagateToStream_Success', Elapsed: 00:10:15
   System.Net.Sockets.Tests: [Long Running Test] 'System.Net.Sockets.Tests.UnixDomainSocketTest.Socket_SendReceiveAsync_PropagateToStream_Success', Elapsed: 00:12:15
   System.Net.Sockets.Tests: [Long Running Test] 'System.Net.Sockets.Tests.UnixDomainSocketTest.Socket_SendReceiveAsync_PropagateToStream_Success', Elapsed: 00:14:15

@ahsonkhan ahsonkhan merged commit 616b849 into dotnet:master May 31, 2019
wtgodbe pushed a commit to dotnet-maestro-bot/corefx that referenced this pull request May 31, 2019
* Add support for immutable dictionaries

* Some clean up

* Address review comments

* Address review comments

* Remove commented out code

* Move object property infos to JsonSerializerOptions

* Re-add immutable test

* Cache IsImmutableDict bool

* More immutable support

* Modify processing enumerable or dictionary check

* Correct handle end dictionary checks

* Re-add dict tests

* React to changes from master
picenka21 pushed a commit to picenka21/runtime that referenced this pull request Feb 18, 2022
* Add support for immutable dictionaries

* Some clean up

* Address review comments

* Address review comments

* Remove commented out code

* Move object property infos to JsonSerializerOptions

* Re-add immutable test

* Cache IsImmutableDict bool

* More immutable support

* Modify processing enumerable or dictionary check

* Correct handle end dictionary checks

* Re-add dict tests

* React to changes from master


Commit migrated from dotnet/corefx@616b849
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.

5 participants