You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by pt...@apache.org on 2020/12/30 07:22:40 UTC

[ignite] branch master updated: IGNITE-13883 .NET: Simplify and optimize TryReadSystemType

This is an automated email from the ASF dual-hosted git repository.

ptupitsyn pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ignite.git


The following commit(s) were added to refs/heads/master by this push:
     new 971f318  IGNITE-13883 .NET: Simplify and optimize TryReadSystemType
971f318 is described below

commit 971f31852a9fbdf14f0fd987d87d344b97298000
Author: Pavel Tupitsyn <pt...@apache.org>
AuthorDate: Wed Dec 30 10:22:08 2020 +0300

    IGNITE-13883 .NET: Simplify and optimize TryReadSystemType
    
    Replace dictionary+interfaces with a simple switch statement in `BinarySystemHandlers.TryReadSystemType`:
    * Less code
    * Simpler
    * Faster
    
    Benchmark added: `BinarySystemTypeReadBenchmark`; results on Core i7-9700K, Ubuntu 20.04, .NET Core 3.1:
    
    ```
    |        |     Mean |     Error |    StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |
    |------- |---------:|----------:|----------:|-------:|------:|------:|----------:|
    | Before | 1.080 us | 0.0030 us | 0.0025 us | 0.0725 |     - |     - |     456 B |
    |  After | 849.4 ns |   4.44 ns | 4.16 ns   | 0.0725 |     - |     - |     456 B |
    ```
    
    (~20% improvement)
---
 .../Apache.Ignite.BenchmarkDotNet.csproj           |   4 +
 .../Apache.Ignite.BenchmarkDotNet.snk              | Bin 0 -> 596 bytes
 .../Binary/BinarySystemTypeReadBenchmark.cs        | 124 +++++++
 .../Apache.Ignite.BenchmarkDotNet/Program.cs       |   4 +-
 .../Apache.Ignite.Core.Tests/Services/Model.cs     |  13 +-
 .../Apache.Ignite.Core/Impl/Binary/BinaryReader.cs |  28 +-
 .../Impl/Binary/BinarySystemHandlers.cs            | 407 +++++++--------------
 .../Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs  |  56 ++-
 .../Apache.Ignite.Core/Properties/AssemblyInfo.cs  |   1 +
 9 files changed, 337 insertions(+), 300 deletions(-)

diff --git a/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Apache.Ignite.BenchmarkDotNet.csproj b/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Apache.Ignite.BenchmarkDotNet.csproj
index 338ddd8..b2ff028 100644
--- a/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Apache.Ignite.BenchmarkDotNet.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Apache.Ignite.BenchmarkDotNet.csproj
@@ -5,6 +5,10 @@
     <TargetFramework>netcoreapp2.0</TargetFramework>
     <ServerGarbageCollection>true</ServerGarbageCollection>
     <GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
+    <AssemblyOriginatorKeyFile>Apache.Ignite.BenchmarkDotNet.snk</AssemblyOriginatorKeyFile>
+    <SignAssembly>true</SignAssembly>
+    <DelaySign>false</DelaySign>
+    <ServerGarbageCollection>true</ServerGarbageCollection>
   </PropertyGroup>
 
   <ItemGroup>
diff --git a/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Apache.Ignite.BenchmarkDotNet.snk b/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Apache.Ignite.BenchmarkDotNet.snk
new file mode 100644
index 0000000..886b6bb
Binary files /dev/null and b/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Apache.Ignite.BenchmarkDotNet.snk differ
diff --git a/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Binary/BinarySystemTypeReadBenchmark.cs b/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Binary/BinarySystemTypeReadBenchmark.cs
new file mode 100644
index 0000000..c40b403
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Binary/BinarySystemTypeReadBenchmark.cs
@@ -0,0 +1,124 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+// ReSharper disable RedundantCast
+namespace Apache.Ignite.BenchmarkDotNet.Binary
+{
+    using System;
+    using System.IO;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Impl.Binary;
+    using Apache.Ignite.Core.Impl.Memory;
+    using global::BenchmarkDotNet.Attributes;
+    using BinaryReader = Apache.Ignite.Core.Impl.Binary.BinaryReader;
+
+    /// <summary>
+    /// System type reading benchmark. Checks <see cref="BinarySystemHandlers.TryReadSystemType{T}"/> performance.
+    /// <para />
+    /// Results on Core i7-9700K, Ubuntu 20.04, .NET Core 3.1:
+    /// | Method |     Mean |   Error |  StdDev |  Gen 0 | Gen 1 | Gen 2 | Allocated |
+    /// |------- |---------:|--------:|--------:|-------:|------:|------:|----------:|
+    /// |   Read | 849.4 ns | 4.44 ns | 4.16 ns | 0.0725 |     - |     - |     456 B |
+    /// </summary>
+    [MemoryDiagnoser]
+    public class BinarySystemTypeReadBenchmark
+    {
+        /** */
+        private static readonly DateTime DateTime = new DateTime(2010, 10, 10).ToUniversalTime();
+
+        /** */
+        private static readonly Guid Guid = Guid.NewGuid();
+
+        /** */
+        private static readonly Marshaller Marsh = new Marshaller(new BinaryConfiguration {ForceTimestamp = true});
+
+        /** */
+        private static readonly PlatformMemoryManager MemMgr = new PlatformMemoryManager(1024);
+
+        /** */
+        private BinaryReader _reader;
+
+        /// <summary>
+        /// Sets up the benchmark.
+        /// </summary>
+        [GlobalSetup]
+        public void Setup()
+        {
+            var mem = MemMgr.Allocate();
+            var stream = mem.GetStream();
+            var writer = Marsh.StartMarshal(stream);
+
+            writer.Write(true);
+            writer.Write('i');
+            writer.Write((byte) 1);
+            writer.Write((short) 2);
+            writer.Write((int) 3);
+            writer.Write((long) 4);
+            writer.Write((float) 5.5);
+            writer.Write((double) 6.6);
+            writer.Write((decimal) 7.7);
+            writer.Write(DateTime);
+            writer.Write(Guid);
+
+            writer.Write(new[] {true});
+            writer.Write(new[] {'i'});
+            writer.Write(new[] {(byte) 1});
+            writer.Write(new[] {(short) 2});
+            writer.Write(new[] {(int) 3});
+            writer.Write(new[] {(long) 4});
+            writer.Write(new[] {(float) 5.5});
+            writer.Write(new[] {(double) 6.6});
+            writer.Write(new[] {(decimal?) 7.7});
+            writer.Write(new DateTime?[] {DateTime});
+            writer.Write(new Guid?[] {Guid});
+
+            stream.SynchronizeOutput();
+
+            _reader = Marsh.StartUnmarshal(stream);
+        }
+
+        [Benchmark]
+        public void Read()
+        {
+            _reader.Stream.Seek(0, SeekOrigin.Begin);
+
+            _reader.ReadObject<bool>();
+            _reader.ReadObject<char>();
+            _reader.ReadObject<byte>();
+            _reader.ReadObject<short>();
+            _reader.ReadObject<int>();
+            _reader.ReadObject<long>();
+            _reader.ReadObject<float>();
+            _reader.ReadObject<double>();
+            _reader.ReadObject<decimal>();
+            _reader.ReadObject<DateTime>();
+            _reader.ReadObject<Guid>();
+
+            _reader.ReadObject<bool[]>();
+            _reader.ReadObject<char[]>();
+            _reader.ReadObject<byte[]>();
+            _reader.ReadObject<short[]>();
+            _reader.ReadObject<int[]>();
+            _reader.ReadObject<long[]>();
+            _reader.ReadObject<float[]>();
+            _reader.ReadObject<double[]>();
+            _reader.ReadObject<decimal?[]>();
+            _reader.ReadObject<DateTime?[]>();
+            _reader.ReadObject<Guid?[]>();
+        }
+    }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Program.cs b/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Program.cs
index d9f53d4..3a1513b 100644
--- a/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Program.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.BenchmarkDotNet/Program.cs
@@ -17,7 +17,7 @@
 
 namespace Apache.Ignite.BenchmarkDotNet
 {
-    using Apache.Ignite.BenchmarkDotNet.ThinClient;
+    using Apache.Ignite.BenchmarkDotNet.Binary;
     using global::BenchmarkDotNet.Running;
 
     /// <summary>
@@ -30,7 +30,7 @@ namespace Apache.Ignite.BenchmarkDotNet
         /// </summary>
         public static void Main()
         {
-            BenchmarkRunner.Run<ThinClientServicesBenchmark>();
+            BenchmarkRunner.Run<BinarySystemTypeReadBenchmark>();
         }
     }
 }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs
index e030098..e239c45 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Services/Model.cs
@@ -94,7 +94,7 @@ namespace org.apache.ignite.platform.model
     public class Account
     {
         public String Id { get; set; }
-        
+
         public int Amount { get; set; }
 
         protected bool Equals(Account other)
@@ -120,6 +120,7 @@ namespace org.apache.ignite.platform.model
     /// <summary>
     /// A enum is a clone of Java class User with the same namespace.
     /// </summary>
+    // ReSharper disable once InconsistentNaming
     public enum ACL
     {
         Allow, Deny
@@ -139,16 +140,16 @@ namespace org.apache.ignite.platform.model
     public class User
     {
         public int Id { get; set; }
-        
+
         public ACL Acl { get; set; }
-        
+
         public Role Role { get; set; }
-    }    
-    
+    }
+
     /// <summary>
     /// A class is a clone of Java class ParamValue with the same namespace.
     /// </summary>
-    public class ParamValue 
+    public class ParamValue
     {
         /** */
         public int Id { get; set; }
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs
index c1a6fed..2d266d6 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs
@@ -29,7 +29,7 @@ namespace Apache.Ignite.Core.Impl.Binary
     using Apache.Ignite.Core.Impl.Common;
 
     /// <summary>
-    /// Binary reader implementation. 
+    /// Binary reader implementation.
     /// </summary>
     internal class BinaryReader : IBinaryReader, IBinaryRawReader
     {
@@ -60,7 +60,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         /// <param name="builder">Builder.</param>
         public BinaryReader
             (Marshaller marsh,
-            IBinaryStream stream, 
+            IBinaryStream stream,
             BinaryMode mode,
             BinaryObjectBuilder builder)
         {
@@ -323,19 +323,19 @@ namespace Apache.Ignite.Core.Impl.Binary
         {
             return Read(stream => BinaryUtils.ReadTimestamp(stream, _marsh.TimestampConverter), BinaryTypeId.Timestamp);
         }
-        
+
         /** <inheritdoc /> */
         public DateTime?[] ReadTimestampArray(string fieldName)
         {
             return ReadField(fieldName, stream => BinaryUtils.ReadTimestampArray(stream, _marsh.TimestampConverter), BinaryTypeId.ArrayTimestamp);
         }
-        
+
         /** <inheritdoc /> */
         public DateTime?[] ReadTimestampArray()
         {
             return Read(stream => BinaryUtils.ReadTimestampArray(stream, _marsh.TimestampConverter), BinaryTypeId.ArrayTimestamp);
         }
-        
+
         /** <inheritdoc /> */
         public string ReadString(string fieldName)
         {
@@ -351,13 +351,13 @@ namespace Apache.Ignite.Core.Impl.Binary
         /** <inheritdoc /> */
         public string[] ReadStringArray(string fieldName)
         {
-            return ReadField(fieldName, r => BinaryUtils.ReadArray<string>(r, false), BinaryTypeId.ArrayString);
+            return ReadField(fieldName, stream => BinaryUtils.ReadStringArray(stream), BinaryTypeId.ArrayString);
         }
 
         /** <inheritdoc /> */
         public string[] ReadStringArray()
         {
-            return Read(r => BinaryUtils.ReadArray<string>(r, false), BinaryTypeId.ArrayString);
+            return Read(stream => BinaryUtils.ReadStringArray(stream), BinaryTypeId.ArrayString);
         }
 
         /** <inheritdoc /> */
@@ -375,13 +375,13 @@ namespace Apache.Ignite.Core.Impl.Binary
         /** <inheritdoc /> */
         public Guid?[] ReadGuidArray(string fieldName)
         {
-            return ReadField(fieldName, r => BinaryUtils.ReadArray<Guid?>(r, false), BinaryTypeId.ArrayGuid);
+            return ReadField(fieldName, stream => BinaryUtils.ReadGuidArray(stream), BinaryTypeId.ArrayGuid);
         }
 
         /** <inheritdoc /> */
         public Guid?[] ReadGuidArray()
         {
-            return Read(r => BinaryUtils.ReadArray<Guid?>(r, false), BinaryTypeId.ArrayGuid);
+            return Read(stream => BinaryUtils.ReadGuidArray(stream), BinaryTypeId.ArrayGuid);
         }
 
         /** <inheritdoc /> */
@@ -410,7 +410,7 @@ namespace Apache.Ignite.Core.Impl.Binary
                     // Unregistered enum written as serializable
                     Stream.Seek(-1, SeekOrigin.Current);
 
-                    return ReadObject<T>(); 
+                    return ReadObject<T>();
 
                 default:
                     throw new BinaryObjectException(string.Format(
@@ -474,7 +474,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         }
 
         /** <inheritdoc /> */
-        public ICollection ReadCollection(string fieldName, Func<int, ICollection> factory, 
+        public ICollection ReadCollection(string fieldName, Func<int, ICollection> factory,
             Action<ICollection, object> adder)
         {
             return ReadField(fieldName, r => BinaryUtils.ReadCollection(r, factory, adder), BinaryTypeId.Collection);
@@ -511,7 +511,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         }
 
         /// <summary>
-        /// Enable detach mode for the next object read. 
+        /// Enable detach mode for the next object read.
         /// </summary>
         public BinaryReader DetachNext()
         {
@@ -800,7 +800,7 @@ namespace Apache.Ignite.Core.Impl.Binary
 
                 if (_frame.Schema == null)
                 {
-                    _frame.Schema = 
+                    _frame.Schema =
                         BinaryObjectSchemaSerializer.GetFieldIds(_frame.Hdr, Marshaller.Ignite, Stream, _frame.Pos);
 
                     desc.Schema.Add(_frame.Hdr.SchemaId, _frame.Schema);
@@ -889,7 +889,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         }
 
         /// <summary>
-        /// Mark current output as raw. 
+        /// Mark current output as raw.
         /// </summary>
         private void MarkRaw()
         {
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs
index 8fdf371..58a4d24 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinarySystemHandlers.cs
@@ -20,8 +20,6 @@ namespace Apache.Ignite.Core.Impl.Binary
     using System;
     using System.Collections;
     using System.Diagnostics;
-    using System.Diagnostics.CodeAnalysis;
-    using Apache.Ignite.Core.Impl.Binary.IO;
     using Apache.Ignite.Core.Impl.Common;
 
     /**
@@ -33,83 +31,13 @@ namespace Apache.Ignite.Core.Impl.Binary
         private static readonly CopyOnWriteConcurrentDictionary<Type, IBinarySystemWriteHandler> WriteHandlers =
             new CopyOnWriteConcurrentDictionary<Type, IBinarySystemWriteHandler>();
 
-        /** Read handlers. */
-        private static readonly IBinarySystemReader[] ReadHandlers = new IBinarySystemReader[255];
-
-        /// <summary>
-        /// Initializes the <see cref="BinarySystemHandlers"/> class.
-        /// </summary>
-        [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", 
-            Justification = "Readability.")]
-        static BinarySystemHandlers()
-        {
-            // 1. Primitives.
-            ReadHandlers[BinaryTypeId.Bool] = new BinarySystemReader<bool>(s => s.ReadBool());
-            ReadHandlers[BinaryTypeId.Byte] = new BinarySystemReader<byte>(s => s.ReadByte());
-            ReadHandlers[BinaryTypeId.Short] = new BinarySystemReader<short>(s => s.ReadShort());
-            ReadHandlers[BinaryTypeId.Char] = new BinarySystemReader<char>(s => s.ReadChar());
-            ReadHandlers[BinaryTypeId.Int] = new BinarySystemReader<int>(s => s.ReadInt());
-            ReadHandlers[BinaryTypeId.Long] = new BinarySystemReader<long>(s => s.ReadLong());
-            ReadHandlers[BinaryTypeId.Float] = new BinarySystemReader<float>(s => s.ReadFloat());
-            ReadHandlers[BinaryTypeId.Double] = new BinarySystemReader<double>(s => s.ReadDouble());
-            ReadHandlers[BinaryTypeId.Decimal] = new BinarySystemReader<decimal?>(BinaryUtils.ReadDecimal);
-
-            // 3. String.
-            ReadHandlers[BinaryTypeId.String] = new BinarySystemReader<string>(BinaryUtils.ReadString);
-
-            // 4. Guid.
-            ReadHandlers[BinaryTypeId.Guid] = new BinarySystemReader<Guid?>(s => BinaryUtils.ReadGuid(s));
-
-            // 5. Primitive arrays.
-            ReadHandlers[BinaryTypeId.ArrayBool] = new BinarySystemReader<bool[]>(BinaryUtils.ReadBooleanArray);
-
-            ReadHandlers[BinaryTypeId.ArrayByte] =
-                new BinarySystemDualReader<byte[], sbyte[]>(BinaryUtils.ReadByteArray, BinaryUtils.ReadSbyteArray);
-            
-            ReadHandlers[BinaryTypeId.ArrayShort] =
-                new BinarySystemDualReader<short[], ushort[]>(BinaryUtils.ReadShortArray,
-                    BinaryUtils.ReadUshortArray);
-
-            ReadHandlers[BinaryTypeId.ArrayChar] = 
-                new BinarySystemReader<char[]>(BinaryUtils.ReadCharArray);
-
-            ReadHandlers[BinaryTypeId.ArrayInt] =
-                new BinarySystemDualReader<int[], uint[]>(BinaryUtils.ReadIntArray, BinaryUtils.ReadUintArray);
-            
-            ReadHandlers[BinaryTypeId.ArrayLong] =
-                new BinarySystemDualReader<long[], ulong[]>(BinaryUtils.ReadLongArray, 
-                    BinaryUtils.ReadUlongArray);
-
-            ReadHandlers[BinaryTypeId.ArrayFloat] =
-                new BinarySystemReader<float[]>(BinaryUtils.ReadFloatArray);
-
-            ReadHandlers[BinaryTypeId.ArrayDouble] =
-                new BinarySystemReader<double[]>(BinaryUtils.ReadDoubleArray);
-
-            ReadHandlers[BinaryTypeId.ArrayDecimal] =
-                new BinarySystemReader<decimal?[]>(BinaryUtils.ReadDecimalArray);
-
-            // 7. String array.
-            ReadHandlers[BinaryTypeId.ArrayString] = new BinarySystemTypedArrayReader<string>();
-
-            // 8. Guid array.
-            ReadHandlers[BinaryTypeId.ArrayGuid] = new BinarySystemTypedArrayReader<Guid?>();
-
-            // 9. Array.
-            ReadHandlers[BinaryTypeId.Array] = new BinarySystemReader(ReadArray);
-
-            // 11. Arbitrary collection.
-            ReadHandlers[BinaryTypeId.Collection] = new BinarySystemReader(ReadCollection);
-
-            // 13. Arbitrary dictionary.
-            ReadHandlers[BinaryTypeId.Dictionary] = new BinarySystemReader(ReadDictionary);
-
-            // 14. Enum. Should be read as Array, see WriteEnumArray implementation.
-            ReadHandlers[BinaryTypeId.ArrayEnum] = new BinarySystemReader(ReadArray);
+        /** */
+        private static readonly BinarySystemWriteHandler<DateTime> TimestampWriteHandler =
+            new BinarySystemWriteHandler<DateTime>(WriteTimestamp, false);
 
-            // 15. Optimized marshaller objects.
-            ReadHandlers[BinaryTypeId.OptimizedMarshaller] = new BinarySystemReader(ReadOptimizedMarshallerObject);
-        }
+        /** */
+        private static readonly BinarySystemWriteHandler<DateTime?[]> TimestampArrayWriteHandler =
+            new BinarySystemWriteHandler<DateTime?[]>(WriteTimestampArray, true);
 
         /// <summary>
         /// Try getting write handler for type.
@@ -122,9 +50,10 @@ namespace Apache.Ignite.Core.Impl.Binary
             if (forceTimestamp)
             {
                 if (type == typeof(DateTime))
-                    return new BinarySystemWriteHandler<DateTime>(WriteTimestamp, false);
+                    return TimestampWriteHandler;
+
                 if (type == typeof(DateTime?[]))
-                    return new BinarySystemWriteHandler<DateTime?[]>(WriteTimestampArray, true);
+                    return TimestampArrayWriteHandler;
             }
 
             return WriteHandlers.GetOrAdd(type, t =>
@@ -192,7 +121,7 @@ namespace Apache.Ignite.Core.Impl.Binary
 
                 // We know how to write any array type.
                 Type elemType = type.GetElementType();
-                
+
                 // Primitives.
                 if (elemType == typeof (bool))
                     return new BinarySystemWriteHandler<bool[]>(WriteBoolArray, true);
@@ -247,32 +176,139 @@ namespace Apache.Ignite.Core.Impl.Binary
         /// </summary>
         public static bool TryReadSystemType<T>(byte typeId, BinaryReader ctx, out T res)
         {
-            var handler = ReadHandlers[typeId];
+            var stream = ctx.Stream;
 
-            if (handler == null)
+            switch (typeId)
             {
-                if (typeId == BinaryTypeId.Timestamp)
-                {
-                    // Date.
-                    res = TypeCaster<T>.Cast(BinaryUtils.ReadTimestamp(ctx.Stream, ctx.Marshaller.TimestampConverter));
+                case BinaryTypeId.Byte:
+                    res = TypeCaster<T>.Cast(stream.ReadByte());
                     return true;
-                }
 
-                if (typeId == BinaryTypeId.ArrayTimestamp)
-                {
-                    // Date array.
-                    res = TypeCaster<T>.Cast(BinaryUtils.ReadTimestampArray(ctx.Stream, ctx.Marshaller.TimestampConverter));
+                case BinaryTypeId.Short:
+                    res = TypeCaster<T>.Cast(stream.ReadShort());
+                    return true;
+
+                case BinaryTypeId.Int:
+                    res = TypeCaster<T>.Cast(stream.ReadInt());
+                    return true;
+
+                case BinaryTypeId.Long:
+                    res = TypeCaster<T>.Cast(stream.ReadLong());
+                    return true;
+
+                case BinaryTypeId.Float:
+                    res = TypeCaster<T>.Cast(stream.ReadFloat());
+                    return true;
+
+                case BinaryTypeId.Double:
+                    res = TypeCaster<T>.Cast(stream.ReadDouble());
+                    return true;
+
+                case BinaryTypeId.Char:
+                    res = TypeCaster<T>.Cast(stream.ReadChar());
+                    return true;
+
+                case BinaryTypeId.Bool:
+                    res = TypeCaster<T>.Cast(stream.ReadBool());
+                    return true;
+
+                case BinaryTypeId.String:
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadString(stream));
+                    return true;
+
+                case BinaryTypeId.Guid:
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadGuid(stream));
+                    return true;
+
+                case BinaryTypeId.ArrayByte:
+                    res = typeof(T) == typeof(sbyte[])
+                        ? TypeCaster<T>.Cast(BinaryUtils.ReadSbyteArray(stream))
+                        : TypeCaster<T>.Cast(BinaryUtils.ReadByteArray(stream));
+                    return true;
+
+                case BinaryTypeId.ArrayShort:
+                    res = typeof(T) == typeof(ushort[])
+                        ? TypeCaster<T>.Cast(BinaryUtils.ReadUshortArray(stream))
+                        : TypeCaster<T>.Cast(BinaryUtils.ReadShortArray(stream));
+                    return true;
+
+                case BinaryTypeId.ArrayInt:
+                    res = typeof(T) == typeof(uint[])
+                        ? TypeCaster<T>.Cast(BinaryUtils.ReadUintArray(stream))
+                        : TypeCaster<T>.Cast(BinaryUtils.ReadIntArray(stream));
+                    return true;
+
+                case BinaryTypeId.ArrayLong:
+                    res = typeof(T) == typeof(ulong[])
+                        ? TypeCaster<T>.Cast(BinaryUtils.ReadUlongArray(stream))
+                        : TypeCaster<T>.Cast(BinaryUtils.ReadLongArray(stream));
+                    return true;
+
+                case BinaryTypeId.ArrayFloat:
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadFloatArray(stream));
+                    return true;
+
+                case BinaryTypeId.ArrayDouble:
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadDoubleArray(stream));
+                    return true;
+
+                case BinaryTypeId.ArrayChar:
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadCharArray(stream));
+                    return true;
+
+                case BinaryTypeId.ArrayBool:
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadBooleanArray(stream));
+                    return true;
+
+                case BinaryTypeId.ArrayString:
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadStringArray(stream));
+                    return true;
+
+                case BinaryTypeId.ArrayGuid:
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadGuidArray(stream));
+                    return true;
+
+                case BinaryTypeId.Array:
+                    res = (T) ReadArray(ctx, typeof(T));
+                    return true;
+
+                case BinaryTypeId.Collection:
+                    res = (T) BinaryUtils.ReadCollection(ctx, null, null);
+                    return true;
+
+                case BinaryTypeId.Dictionary:
+                    res = (T) BinaryUtils.ReadDictionary(ctx, null);
+                    return true;
+
+                case BinaryTypeId.ArrayEnum:
+                    res = (T) ReadArray(ctx, typeof(T));
+                    return true;
+
+                case BinaryTypeId.Decimal:
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadDecimal(stream));
+                    return true;
+
+                case BinaryTypeId.ArrayDecimal:
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadDecimalArray(stream));
+                    return true;
+
+                case BinaryTypeId.Timestamp:
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadTimestamp(stream, ctx.Marshaller.TimestampConverter));
+                    return true;
+
+                case BinaryTypeId.ArrayTimestamp:
+                    res = TypeCaster<T>.Cast(BinaryUtils.ReadTimestampArray(stream, ctx.Marshaller.TimestampConverter));
                     return true;
-                }
 
-                res = default(T);
-                return false;
+                case BinaryTypeId.OptimizedMarshaller:
+                    res = (T) (object) new OptimizedMarshallerObject(ctx.Stream);
+                    return true;
             }
 
-            res = handler.Read<T>(ctx);
-            return true;
+            res = default(T);
+            return false;
         }
-        
+
         /// <summary>
         /// Write decimal.
         /// </summary>
@@ -284,7 +320,7 @@ namespace Apache.Ignite.Core.Impl.Binary
 
             BinaryUtils.WriteDecimal(obj, ctx.Stream);
         }
-        
+
         /// <summary>
         /// Write string.
         /// </summary>
@@ -332,7 +368,7 @@ namespace Apache.Ignite.Core.Impl.Binary
 
             BinaryUtils.WriteBooleanArray(obj, ctx.Stream);
         }
-        
+
         /// <summary>
         /// Write byte array.
         /// </summary>
@@ -356,7 +392,7 @@ namespace Apache.Ignite.Core.Impl.Binary
 
             BinaryUtils.WriteShortArray(obj, ctx.Stream);
         }
-        
+
         /// <summary>
         /// Write char array.
         /// </summary>
@@ -428,7 +464,7 @@ namespace Apache.Ignite.Core.Impl.Binary
 
             BinaryUtils.WriteDecimalArray(obj, ctx.Stream);
         }
-        
+
         /// <summary>
         /// Write string array.
         /// </summary>
@@ -440,7 +476,7 @@ namespace Apache.Ignite.Core.Impl.Binary
 
             BinaryUtils.WriteStringArray(obj, ctx.Stream);
         }
-        
+
         /// <summary>
         /// Write nullable GUID array.
         /// </summary>
@@ -514,7 +550,7 @@ namespace Apache.Ignite.Core.Impl.Binary
 
             BinaryUtils.WriteBinary(ctx.Stream, obj);
         }
-        
+
         /// <summary>
         /// Write enum.
         /// </summary>
@@ -557,22 +593,6 @@ namespace Apache.Ignite.Core.Impl.Binary
             return BinaryUtils.ReadTypedArray(ctx, true, elemType);
         }
 
-        /**
-         * <summary>Read collection.</summary>
-         */
-        private static object ReadCollection(BinaryReader ctx, Type type)
-        {
-            return BinaryUtils.ReadCollection(ctx, null, null);
-        }
-
-        /**
-         * <summary>Read dictionary.</summary>
-         */
-        private static object ReadDictionary(BinaryReader ctx, Type type)
-        {
-            return BinaryUtils.ReadDictionary(ctx, null);
-        }
-                
         /// <summary>
         /// Write Ignite.
         /// </summary>
@@ -580,151 +600,6 @@ namespace Apache.Ignite.Core.Impl.Binary
         {
             ctx.Stream.WriteByte(BinaryUtils.HdrNull);
         }
-
-        /// <summary>
-        /// Reads the optimized marshaller object.
-        /// </summary>
-        private static object ReadOptimizedMarshallerObject(BinaryReader ctx, Type type)
-        {
-            return new OptimizedMarshallerObject(ctx.Stream);
-        }
-
-        /**
-         * <summary>Read delegate.</summary>
-         * <param name="ctx">Read context.</param>
-         * <param name="type">Type.</param>
-         */
-        private delegate object BinarySystemReadDelegate(BinaryReader ctx, Type type);
-
-        /// <summary>
-        /// System type reader.
-        /// </summary>
-        private interface IBinarySystemReader
-        {
-            /// <summary>
-            /// Reads a value of specified type from reader.
-            /// </summary>
-            T Read<T>(BinaryReader ctx);
-        }
-
-        /// <summary>
-        /// System type generic reader.
-        /// </summary>
-        private interface IBinarySystemReader<out T>
-        {
-            /// <summary>
-            /// Reads a value of specified type from reader.
-            /// </summary>
-            T Read(BinaryReader ctx);
-        }
-
-        /// <summary>
-        /// Default reader with boxing.
-        /// </summary>
-        private class BinarySystemReader : IBinarySystemReader
-        {
-            /** */
-            private readonly BinarySystemReadDelegate _readDelegate;
-
-            /// <summary>
-            /// Initializes a new instance of the <see cref="BinarySystemReader"/> class.
-            /// </summary>
-            /// <param name="readDelegate">The read delegate.</param>
-            public BinarySystemReader(BinarySystemReadDelegate readDelegate)
-            {
-                Debug.Assert(readDelegate != null);
-
-                _readDelegate = readDelegate;
-            }
-
-            /** <inheritdoc /> */
-            public T Read<T>(BinaryReader ctx)
-            {
-                return (T)_readDelegate(ctx, typeof(T));
-            }
-        }
-
-        /// <summary>
-        /// Reader without boxing.
-        /// </summary>
-        private class BinarySystemReader<T> : IBinarySystemReader
-        {
-            /** */
-            private readonly Func<IBinaryStream, T> _readDelegate;
-
-            /// <summary>
-            /// Initializes a new instance of the <see cref="BinarySystemReader{T}"/> class.
-            /// </summary>
-            /// <param name="readDelegate">The read delegate.</param>
-            public BinarySystemReader(Func<IBinaryStream, T> readDelegate)
-            {
-                Debug.Assert(readDelegate != null);
-
-                _readDelegate = readDelegate;
-            }
-
-            /** <inheritdoc /> */
-            public TResult Read<TResult>(BinaryReader ctx)
-            {
-                return TypeCaster<TResult>.Cast(_readDelegate(ctx.Stream));
-            }
-        }
-
-        /// <summary>
-        /// Reader without boxing.
-        /// </summary>
-        private class BinarySystemTypedArrayReader<T> : IBinarySystemReader
-        {
-            public TResult Read<TResult>(BinaryReader ctx)
-            {
-                return TypeCaster<TResult>.Cast(BinaryUtils.ReadArray<T>(ctx, false));
-            }
-        }
-
-        /// <summary>
-        /// Reader with selection based on requested type.
-        /// </summary>
-        private class BinarySystemDualReader<T1, T2> : IBinarySystemReader, IBinarySystemReader<T2>
-        {
-            /** */
-            private readonly Func<IBinaryStream, T1> _readDelegate1;
-
-            /** */
-            private readonly Func<IBinaryStream, T2> _readDelegate2;
-
-            /// <summary>
-            /// Initializes a new instance of the <see cref="BinarySystemDualReader{T1,T2}"/> class.
-            /// </summary>
-            /// <param name="readDelegate1">The read delegate1.</param>
-            /// <param name="readDelegate2">The read delegate2.</param>
-            public BinarySystemDualReader(Func<IBinaryStream, T1> readDelegate1, Func<IBinaryStream, T2> readDelegate2)
-            {
-                Debug.Assert(readDelegate1 != null);
-                Debug.Assert(readDelegate2 != null);
-
-                _readDelegate1 = readDelegate1;
-                _readDelegate2 = readDelegate2;
-            }
-
-            /** <inheritdoc /> */
-            [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods")]
-            T2 IBinarySystemReader<T2>.Read(BinaryReader ctx)
-            {
-                return _readDelegate2(ctx.Stream);
-            }
-
-            /** <inheritdoc /> */
-            public T Read<T>(BinaryReader ctx)
-            {
-                // Can't use "as" because of variance. 
-                // For example, IBinarySystemReader<byte[]> can be cast to IBinarySystemReader<sbyte[]>, which
-                // will cause incorrect behavior.
-                if (typeof (T) == typeof (T2))  
-                    return ((IBinarySystemReader<T>) this).Read(ctx);
-
-                return TypeCaster<T>.Cast(_readDelegate1(ctx.Stream));
-            }
-        }
     }
 
     /// <summary>
@@ -748,7 +623,7 @@ namespace Apache.Ignite.Core.Impl.Binary
     /// <summary>
     /// Write delegate + handles flag.
     /// </summary>
-    internal class BinarySystemWriteHandler<T1> : IBinarySystemWriteHandler
+    internal sealed class BinarySystemWriteHandler<T1> : IBinarySystemWriteHandler
     {
         /** */
         private readonly Action<BinaryWriter, T1> _writeAction;
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs
index 82e2284..a60411f 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryUtils.cs
@@ -110,9 +110,9 @@ namespace Apache.Ignite.Core.Impl.Binary
         /** String mode. */
         public static readonly bool UseStringSerializationVer2 =
             (Environment.GetEnvironmentVariable(IgniteBinaryMarshallerUseStringSerializationVer2) ?? "false") == "true";
-        
+
         /** Cached maps of enum members per type. */
-        private static readonly CopyOnWriteConcurrentDictionary<Type, Dictionary<string, int>> EnumValues = 
+        private static readonly CopyOnWriteConcurrentDictionary<Type, Dictionary<string, int>> EnumValues =
             new CopyOnWriteConcurrentDictionary<Type, Dictionary<string, int>>();
 
         /// <summary>
@@ -926,7 +926,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         }
 
         /// <summary>
-        /// Writes a guid with bitwise conversion, assuming that <see cref="Guid"/> 
+        /// Writes a guid with bitwise conversion, assuming that <see cref="Guid"/>
         /// is laid out in memory sequentially and without gaps between fields.
         /// </summary>
         /// <param name="val">The value.</param>
@@ -974,7 +974,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         }
 
         /// <summary>
-        /// Reads a guid with bitwise conversion, assuming that <see cref="Guid"/> 
+        /// Reads a guid with bitwise conversion, assuming that <see cref="Guid"/>
         /// is laid out in memory sequentially and without gaps between fields.
         /// </summary>
         /// <param name="stream">The stream.</param>
@@ -1103,8 +1103,8 @@ namespace Apache.Ignite.Core.Impl.Binary
         /// </summary>
         public static int GetArrayElementTypeId(Type elemType, Marshaller marsh)
         {
-            return elemType == typeof(object) 
-                ? ObjTypeId 
+            return elemType == typeof(object)
+                ? ObjTypeId
                 : marsh.GetDescriptor(elemType).TypeId;
         }
 
@@ -1171,6 +1171,38 @@ namespace Apache.Ignite.Core.Impl.Binary
         }
 
         /// <summary>
+        /// Read string array.
+        /// </summary>
+        /// <param name="stream">Stream</param>
+        /// <returns>String array.</returns>
+        public static string[] ReadStringArray(IBinaryStream stream)
+        {
+            var len = stream.ReadInt();
+            var res = new string[len];
+
+            for (var i = 0; i < len; i++)
+                res[i] = stream.ReadByte() == HdrNull ? null : ReadString(stream);
+
+            return res;
+        }
+
+        /// <summary>
+        /// Read string array.
+        /// </summary>
+        /// <param name="stream">Stream</param>
+        /// <returns>String array.</returns>
+        public static Guid?[] ReadGuidArray(IBinaryStream stream)
+        {
+            var len = stream.ReadInt();
+            var res = new Guid?[len];
+
+            for (var i = 0; i < len; i++)
+                res[i] = stream.ReadByte() == HdrNull ? (Guid?) null : ReadGuid(stream);
+
+            return res;
+        }
+
+        /// <summary>
         /// Read timestamp array.
         /// </summary>
         /// <param name="stream">Stream.</param>
@@ -1596,7 +1628,7 @@ namespace Apache.Ignite.Core.Impl.Binary
             err = null;
 
             if (reader.ReadBoolean()) // success indication
-                return reader.ReadObject<object>(); 
+                return reader.ReadObject<object>();
 
             err = reader.ReadBoolean() // native error indication
                 ? reader.ReadObject<object>()
@@ -1746,7 +1778,7 @@ namespace Apache.Ignite.Core.Impl.Binary
 
             var enumType = Enum.GetUnderlyingType(type);
 
-            return enumType == typeof(int) || enumType == typeof(byte) || enumType == typeof(sbyte) 
+            return enumType == typeof(int) || enumType == typeof(byte) || enumType == typeof(sbyte)
                 || enumType == typeof(short) || enumType == typeof(ushort) || enumType == typeof(uint);
         }
 
@@ -1763,7 +1795,7 @@ namespace Apache.Ignite.Core.Impl.Binary
 
             return TimeSpan.FromMilliseconds(ms);
         }
-        
+
         /// <summary>
         /// Gets the enum values.
         /// </summary>
@@ -1771,7 +1803,7 @@ namespace Apache.Ignite.Core.Impl.Binary
         {
             Debug.Assert(enumType != null);
             Debug.Assert(enumType.IsEnum);
-            
+
             Dictionary<string,int> res;
             if (EnumValues.TryGetValue(enumType, out res))
             {
@@ -1792,10 +1824,10 @@ namespace Apache.Ignite.Core.Impl.Binary
             }
 
             EnumValues.Set(enumType, res);
-            
+
             return res;
         }
-        
+
         /// <summary>
         /// Gets the enum value as int.
         /// </summary>
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs
index 608a527..63b7ee3 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Properties/AssemblyInfo.cs
@@ -48,5 +48,6 @@ using System.Runtime.InteropServices;
 [assembly: InternalsVisibleTo("Apache.Ignite, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e3e6312f7ca1ab31b8ac8408668fc85b4a76a689f7eef8553d491a594cd9ea29432b7aea12ed8f34925b2db1e01fa64f333c4e8866252a2b2d512afa5d2e06eef94fb2cdd05904f2ad70d3752fbf531bef128ecad9e699f54b2cfcd0b394b7b40e33b8c7ed45a2cd300e1b4d01c8ec7fd753683594a18f396727c304920a5e97")]
 #if NETCOREAPP
 [assembly: InternalsVisibleTo("Apache.Ignite.Core.Tests.DotNetCore, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c9b65cf83e050e9d360a840fc6632774936ea62a9a14db0aa1b991ac6b7c94030a647be9755d6b3b5883367c58cb834866b4f3acbdb5881dff677f2c046ed7cae85c9a62a7737f377813ebe4ae19f89d357e811aa577c909b82d5f02a9b05dab39da3ff92927ce0b0c4d0204961ee5d5c329274b68f19ebba129ee8e3fd0d0c1")]
+[assembly: InternalsVisibleTo("Apache.Ignite.BenchmarkDotNet, PublicKey=0024000004800000940000000602000000240000525341310004000001000100a3e0c1df4cbedbd4ed0e88808401c69b69ec12575ed1c056ac9f448e018fb29af19d236b7b03563aad66c48ab2045e72971ed098d4f65d4cdd38d65abcb39b4f84c626b22ccab2754375f0e8c97dc304fa146f0eddad5cc40a71803a8f15b0b0bb0bff0d4bf0ff6a64bb1044e0d71e6e2405b83fd4c1f7b3e2cfc2e9d50823d4")]
 #endif
 #endif