You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by vo...@apache.org on 2015/10/22 11:57:48 UTC

ignite git commit: IGNITE-1665: Avoid field ID re-calculation during reads.

Repository: ignite
Updated Branches:
  refs/heads/ignite-1282 d85fa945f -> f85c6a3e6


IGNITE-1665: Avoid field ID re-calculation during reads.


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/f85c6a3e
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/f85c6a3e
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/f85c6a3e

Branch: refs/heads/ignite-1282
Commit: f85c6a3e6b81e9a3c22bcb5a301cad1dc5b57e8a
Parents: d85fa94
Author: Pavel Tupitsyn <pt...@gridgain.com>
Authored: Thu Oct 22 12:58:29 2015 +0300
Committer: vozerov-gridgain <vo...@gridgain.com>
Committed: Thu Oct 22 12:58:29 2015 +0300

----------------------------------------------------------------------
 .../Apache.Ignite.Benchmarks.csproj             |   1 +
 .../Apache.Ignite.Benchmarks/BenchmarkRunner.cs |   2 +-
 .../Portable/PortableReadBenchmark.cs           | 126 ++++++++++++++++++
 .../Portable/PortableStructureTest.cs           |  21 +--
 .../Apache.Ignite.Core.csproj                   |   1 +
 .../Impl/Portable/IPortableTypeDescriptor.cs    |  21 ++-
 .../Impl/Portable/PortableFullTypeDescriptor.cs |  29 +++-
 .../Impl/Portable/PortableReaderImpl.cs         |  26 ++--
 .../Portable/PortableSurrogateTypeDescriptor.cs |  27 +++-
 .../Impl/Portable/PortableUtils.cs              |  12 +-
 .../Impl/Portable/PortableWriterImpl.cs         |  79 +----------
 .../Portable/Structure/PortableStructure.cs     |   4 +-
 .../Structure/PortableStructureEntry.cs         |   2 +-
 .../Structure/PortableStructureTracker.cs       | 132 +++++++++++++++++++
 14 files changed, 355 insertions(+), 128 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Apache.Ignite.Benchmarks.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Apache.Ignite.Benchmarks.csproj b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Apache.Ignite.Benchmarks.csproj
index a85b189..26e4fff 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Apache.Ignite.Benchmarks.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Apache.Ignite.Benchmarks.csproj
@@ -57,6 +57,7 @@
     <Compile Include="Model\Employee.cs" />
     <Compile Include="Model\Sex.cs" />
     <Compile Include="Model\TestModel.cs" />
+    <Compile Include="Portable\PortableReadBenchmark.cs" />
     <Compile Include="Portable\PortableWriteBenchmark.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
     <Compile Include="Result\BenchmarkConsoleResultWriter.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Benchmarks/BenchmarkRunner.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/BenchmarkRunner.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/BenchmarkRunner.cs
index 506106e..2d0d348 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/BenchmarkRunner.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/BenchmarkRunner.cs
@@ -35,7 +35,7 @@ namespace Apache.Ignite.Benchmarks
         public static void Main(string[] args)
         {
             args = new[] { 
-                typeof(PortableWriteBenchmark).FullName,
+                typeof(PortableReadBenchmark).FullName,
                 "-ConfigPath", @"modules\platforms\dotnet\Apache.Ignite.Benchmarks\Config\benchmark.xml",
                 "-Threads", "1",
                 "-Warmup", "0",

http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableReadBenchmark.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableReadBenchmark.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableReadBenchmark.cs
new file mode 100644
index 0000000..4df225e
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableReadBenchmark.cs
@@ -0,0 +1,126 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Benchmarks.Portable
+{
+    using System;
+    using System.Collections.Generic;
+    using System.Linq;
+    using Apache.Ignite.Benchmarks.Model;
+    using Apache.Ignite.Core.Impl.Memory;
+    using Apache.Ignite.Core.Impl.Portable;
+    using Apache.Ignite.Core.Portable;
+
+    /// <summary>
+    /// Portable read benchmark.
+    /// </summary>
+    internal class PortableReadBenchmark : BenchmarkBase
+    {
+        /** Marshaller. */
+        private readonly PortableMarshaller _marsh;
+
+        /** Memory manager. */
+        private readonly PlatformMemoryManager _memMgr = new PlatformMemoryManager(1024);
+
+        /** Memory chunk. */
+        private readonly IPlatformMemory _mem;
+
+        /** Pre-allocated address. */
+        private readonly Address _address = BenchmarkUtils.GetRandomAddress();
+
+
+        /** Pre-allocated model. */
+        private readonly TestModel _model = new TestModel
+        {
+            Byte = 5,
+            Boolean = true,
+            BooleanArray = new[] {true, false, false, false, true, true},
+            ByteArray = new byte[] {128, 1, 2, 3, 5, 6, 8, 9, 14},
+            Char = 'h',
+            CharArray = new[] {'b', 'n', 'm', 'q', 'w', 'e', 'r', 't', 'y'},
+            Date = DateTime.Now,
+            DateArray = Enumerable.Range(1, 15).Select(x => (DateTime?) DateTime.Now.AddDays(x)).ToArray(),
+            Decimal = decimal.MinValue,
+            DecimalArray = new decimal?[] {1.1M, decimal.MinValue, decimal.MaxValue, decimal.MinusOne, decimal.One},
+            Double = double.MaxValue/2,
+            DoubleArray = new[] {double.MaxValue, double.MinValue, double.Epsilon, double.NegativeInfinity},
+            Float = 98,
+            FloatArray = new[] {float.MinValue, float.MaxValue, 10F, 36F},
+            Guid = Guid.NewGuid(),
+            GuidArray = Enumerable.Range(1, 9).Select(x => (Guid?) Guid.NewGuid()).ToArray(),
+            Int = -90,
+            IntArray = new[] {128, 1, 2, 3, 5, 6, 8, 9, 14},
+            Long = long.MinValue,
+            LongArray = Enumerable.Range(1, 12).Select(x => (long) x).ToArray(),
+            Short = 67,
+            ShortArray = Enumerable.Range(100, 12).Select(x => (short) x).ToArray(),
+            String = "String value test 123",
+            StringArray = Enumerable.Range(1, 13).Select(x => Guid.NewGuid().ToString()).ToArray()
+        };
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="PortableReadBenchmark"/> class.
+        /// </summary>
+        public PortableReadBenchmark()
+        {
+            _marsh = new PortableMarshaller(new PortableConfiguration
+            {
+                TypeConfigurations = new List<PortableTypeConfiguration>
+                {
+                    new PortableTypeConfiguration(typeof (Address)) {MetadataEnabled = true},
+                    new PortableTypeConfiguration(typeof (TestModel)) {MetadataEnabled = false}
+                }
+            });
+
+            _mem = _memMgr.Allocate();
+
+            var stream = _mem.GetStream();
+
+            //_marsh.StartMarshal(stream).Write(_model);
+            _marsh.StartMarshal(stream).Write(_address);
+
+            stream.SynchronizeOutput();
+        }
+
+        /// <summary>
+        /// Populate descriptors.
+        /// </summary>
+        /// <param name="descs">Descriptors.</param>
+        protected override void GetDescriptors(ICollection<BenchmarkOperationDescriptor> descs)
+        {
+            descs.Add(BenchmarkOperationDescriptor.Create("ReadTestModel", ReadTestModel, 1));
+        }
+
+        /// <summary>
+        /// Write address.
+        /// </summary>
+        /// <param name="state">State.</param>
+        private void ReadTestModel(BenchmarkState state)
+        {
+            //var model = _marsh.StartUnmarshal(_mem.GetStream()).ReadObject<TestModel>();
+
+            //if (model.Byte != _model.Byte)
+            //    throw new InvalidOperationException();
+
+            var model = _marsh.StartUnmarshal(_mem.GetStream()).ReadObject<Address>();
+
+            if (model.FlatNumber != _address.FlatNumber)
+                throw new InvalidOperationException();
+
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableStructureTest.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableStructureTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableStructureTest.cs
index 580522e..4baebde 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableStructureTest.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableStructureTest.cs
@@ -19,9 +19,9 @@ namespace Apache.Ignite.Core.Tests.Portable
 {
     using System;
     using System.Collections.Generic;
+    using System.Diagnostics.CodeAnalysis;
     using Apache.Ignite.Core.Impl;
     using Apache.Ignite.Core.Impl.Portable;
-    using Apache.Ignite.Core.Impl.Portable.Structure;
     using Apache.Ignite.Core.Portable;
     using NUnit.Framework;
 
@@ -80,26 +80,15 @@ namespace Apache.Ignite.Core.Tests.Portable
                 Console.WriteLine();
 
                 // 4. Ensure that all fields are recorded.
-                IPortableTypeDescriptor desc = marsh.GetDescriptor(typeof (BranchedType));
+                var desc = marsh.GetDescriptor(typeof (BranchedType));
 
-                PortableStructure typeStruct = desc.TypeStructure;
-
-                IDictionary<string, byte> fields = typeStruct.FieldTypes;
-
-                Assert.IsTrue(fields.Count == 8);
-
-                Assert.IsTrue(fields.ContainsKey("mode"));
-                Assert.IsTrue(fields.ContainsKey("f2"));
-                Assert.IsTrue(fields.ContainsKey("f3"));
-                Assert.IsTrue(fields.ContainsKey("f4"));
-                Assert.IsTrue(fields.ContainsKey("f5"));
-                Assert.IsTrue(fields.ContainsKey("f6"));
-                Assert.IsTrue(fields.ContainsKey("f7"));
-                Assert.IsTrue(fields.ContainsKey("f8"));
+                CollectionAssert.AreEquivalent(new[] {"mode", "f2", "f3", "f4", "f5", "f6", "f7", "f8"},
+                    desc.WriterTypeStructure.FieldTypes.Keys);
             }
         }
     }
 
+    [SuppressMessage("ReSharper", "InconsistentNaming")]
     public class BranchedType : IPortableMarshalAware
     {
         public int mode;

http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
index 9bcb41a..a45d6ed 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Apache.Ignite.Core.csproj
@@ -270,6 +270,7 @@
     <Compile Include="Impl\Portable\PortableReflectiveRoutines.cs" />
     <Compile Include="Impl\Portable\PortableReflectiveSerializer.cs" />
     <Compile Include="Impl\Portable\PortablesImpl.cs" />
+    <Compile Include="Impl\Portable\Structure\PortableStructureTracker.cs" />
     <Compile Include="Impl\Portable\PortableSurrogateTypeDescriptor.cs" />
     <Compile Include="Impl\Portable\PortableSystemHandlers.cs" />
     <Compile Include="Impl\Portable\PortableSystemTypeSerializer.cs" />

http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/IPortableTypeDescriptor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/IPortableTypeDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/IPortableTypeDescriptor.cs
index 81b6c8d..3872773 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/IPortableTypeDescriptor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/IPortableTypeDescriptor.cs
@@ -109,16 +109,29 @@ namespace Apache.Ignite.Core.Impl.Portable
         }
 
         /// <summary>
-        /// Type structure.
+        /// Write type structure.
         /// </summary>
-        PortableStructure TypeStructure { get; }
+        PortableStructure WriterTypeStructure { get; }
 
         /// <summary>
-        /// Update type structure.
+        /// Read type structure.
+        /// </summary>
+        PortableStructure ReaderTypeStructure { get; }
+
+        /// <summary>
+        /// Update write type structure.
+        /// </summary>
+        /// <param name="exp">Expected type structure.</param>
+        /// <param name="pathIdx">Path index.</param>
+        /// <param name="updates">Recorded updates.</param>
+        void UpdateWriteStructure(PortableStructure exp, int pathIdx, IList<PortableStructureUpdate> updates);
+
+        /// <summary>
+        /// Update read type structure.
         /// </summary>
         /// <param name="exp">Expected type structure.</param>
         /// <param name="pathIdx">Path index.</param>
         /// <param name="updates">Recorded updates.</param>
-        void UpdateStructure(PortableStructure exp, int pathIdx, IList<PortableStructureUpdate> updates);
+        void UpdateReadStructure(PortableStructure exp, int pathIdx, IList<PortableStructureUpdate> updates);
     }
 }

http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableFullTypeDescriptor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableFullTypeDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableFullTypeDescriptor.cs
index ecad8ba..312cefa 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableFullTypeDescriptor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableFullTypeDescriptor.cs
@@ -59,7 +59,10 @@ namespace Apache.Ignite.Core.Impl.Portable
         private readonly string _affKeyFieldName;
 
         /** Type structure. */
-        private volatile PortableStructure _typeStruct = PortableStructure.CreateEmpty();
+        private volatile PortableStructure _writerTypeStruct = PortableStructure.CreateEmpty();
+
+        /** Type structure. */
+        private volatile PortableStructure _readerTypeStructure = PortableStructure.CreateEmpty();
 
         /// <summary>
         /// Constructor.
@@ -179,18 +182,34 @@ namespace Apache.Ignite.Core.Impl.Portable
         }
 
         /** <inheritDoc /> */
-        public PortableStructure TypeStructure
+        public PortableStructure WriterTypeStructure
+        {
+            get { return _writerTypeStruct; }
+        }
+
+        /** <inheritDoc /> */
+        public PortableStructure ReaderTypeStructure
+        {
+            get { return _readerTypeStructure; }
+        }
+
+        /** <inheritDoc /> */
+        public void UpdateWriteStructure(PortableStructure exp, int pathIdx, 
+            IList<PortableStructureUpdate> updates)
         {
-            get { return _typeStruct; }
+            lock (this)
+            {
+                _writerTypeStruct = _writerTypeStruct.Merge(exp, pathIdx, updates);
+            }
         }
 
         /** <inheritDoc /> */
-        public void UpdateStructure(PortableStructure exp, int pathIdx, 
+        public void UpdateReadStructure(PortableStructure exp, int pathIdx, 
             IList<PortableStructureUpdate> updates)
         {
             lock (this)
             {
-                _typeStruct = _typeStruct.Merge(exp, pathIdx, updates);
+                _readerTypeStructure = _readerTypeStructure.Merge(exp, pathIdx, updates);
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReaderImpl.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReaderImpl.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReaderImpl.cs
index ff9aa34..dd78702 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReaderImpl.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableReaderImpl.cs
@@ -25,6 +25,7 @@ namespace Apache.Ignite.Core.Impl.Portable
     using System.Runtime.Serialization;
     using Apache.Ignite.Core.Impl.Common;
     using Apache.Ignite.Core.Impl.Portable.IO;
+    using Apache.Ignite.Core.Impl.Portable.Structure;
     using Apache.Ignite.Core.Portable;
 
     /// <summary>
@@ -53,12 +54,6 @@ namespace Apache.Ignite.Core.Impl.Portable
         /** Current raw data offset. */
         private int _curRawOffset;
 
-        /** Current converter. */
-        private IPortableNameMapper _curConverter;
-
-        /** Current mapper. */
-        private IPortableIdMapper _curMapper;
-
         /** Current raw flag. */
         private bool _curRaw;
 
@@ -68,6 +63,10 @@ namespace Apache.Ignite.Core.Impl.Portable
         /** Portable read mode. */
         private PortableMode _mode;
 
+        /** Current type structure tracker. */
+        private PortableStructureTracker _curStruct;
+
+
         /// <summary>
         /// Constructor.
         /// </summary>
@@ -449,7 +448,7 @@ namespace Apache.Ignite.Core.Impl.Portable
             if (_curRaw)
                 throw new PortableException("Cannot read named fields after raw data is read.");
 
-            int fieldId = PortableUtils.FieldId(_curTypeId, fieldName, _curConverter, _curMapper);
+            int fieldId = _curStruct.GetFieldId(fieldName);
 
             if (SeekField(fieldId))
                 return Deserialize<T>();
@@ -747,16 +746,14 @@ namespace Apache.Ignite.Core.Impl.Portable
                     int oldTypeId = _curTypeId;
                     int oldPos = _curPos;
                     int oldRawOffset = _curRawOffset;
-                    IPortableNameMapper oldConverter = _curConverter;
-                    IPortableIdMapper oldMapper = _curMapper;
+                    var oldStruct = _curStruct;
                     bool oldRaw = _curRaw;
 
                     // Set new frame.
                     _curTypeId = typeId;
                     _curPos = pos;
                     _curRawOffset = rawOffset;
-                    _curConverter = desc.NameConverter;
-                    _curMapper = desc.Mapper;
+                    _curStruct = new PortableStructureTracker(desc, desc.ReaderTypeStructure);
                     _curRaw = false;
 
                     // Read object.
@@ -784,12 +781,13 @@ namespace Apache.Ignite.Core.Impl.Portable
                         desc.Serializer.ReadPortable(obj, this);
                     }
 
+                    _curStruct.UpdateReaderStructure();
+
                     // Restore old frame.
                     _curTypeId = oldTypeId;
                     _curPos = oldPos;
                     _curRawOffset = oldRawOffset;
-                    _curConverter = oldConverter;
-                    _curMapper = oldMapper;
+                    _curStruct = oldStruct;
                     _curRaw = oldRaw;
 
                     var wrappedSerializable = obj as SerializableObjectHolder;
@@ -952,7 +950,7 @@ namespace Apache.Ignite.Core.Impl.Portable
             if (_curRaw)
                 throw new PortableException("Cannot read named fields after raw data is read.");
 
-            var fieldId = PortableUtils.FieldId(_curTypeId, fieldName, _curConverter, _curMapper);
+            var fieldId = _curStruct.GetFieldId(fieldName);
 
             if (!SeekField(fieldId))
                 return false;

http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs
index 8dc18b6..8560ce8 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableSurrogateTypeDescriptor.cs
@@ -39,7 +39,10 @@ namespace Apache.Ignite.Core.Impl.Portable
         private readonly string _name;
 
         /** Type structure. */
-        private volatile PortableStructure _typeStruct = PortableStructure.CreateEmpty();
+        private volatile PortableStructure _writerTypeStruct = PortableStructure.CreateEmpty();
+
+        /** Type structure. */
+        private PortableStructure _readerTypeStructure = PortableStructure.CreateEmpty();
 
         /// <summary>
         /// Constructor.
@@ -126,17 +129,31 @@ namespace Apache.Ignite.Core.Impl.Portable
         }
 
         /** <inheritDoc /> */
-        public PortableStructure TypeStructure
+        public PortableStructure WriterTypeStructure
+        {
+            get { return _writerTypeStruct; }
+        }
+
+        public PortableStructure ReaderTypeStructure
+        {
+            get { return _readerTypeStructure; }
+        }
+
+        /** <inheritDoc /> */
+        public void UpdateWriteStructure(PortableStructure exp, int pathIdx, IList<PortableStructureUpdate> updates)
         {
-            get { return _typeStruct; }
+            lock (this)
+            {
+                _writerTypeStruct = _writerTypeStruct.Merge(exp, pathIdx, updates);
+            }
         }
 
         /** <inheritDoc /> */
-        public void UpdateStructure(PortableStructure exp, int pathIdx, IList<PortableStructureUpdate> updates)
+        public void UpdateReadStructure(PortableStructure exp, int pathIdx, IList<PortableStructureUpdate> updates)
         {
             lock (this)
             {
-                _typeStruct = _typeStruct.Merge(exp, pathIdx, updates);
+                _readerTypeStructure = _readerTypeStructure.Merge(exp, pathIdx, updates);
             }
         }
     }

http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableUtils.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableUtils.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableUtils.cs
index 15466d5..ed8d5e1 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableUtils.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableUtils.cs
@@ -1558,16 +1558,14 @@ namespace Apache.Ignite.Core.Impl.Portable
         {
             if (val == null)
                 return 0;
+
             int hash = 0;
 
-            for (int i = 0; i < val.Length; i++)
+            unchecked
             {
-                char c = val[i];
-
-                if ('A' <= c && c <= 'Z')
-                    c = (char)(c | 0x20);
-
-                hash = 31 * hash + c;
+                // ReSharper disable once LoopCanBeConvertedToQuery (performance)
+                foreach (var c in val)
+                    hash = 31 * hash + ('A' <= c && c <= 'Z' ? c | 0x20 : c);
             }
 
             return hash;

http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableWriterImpl.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableWriterImpl.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableWriterImpl.cs
index 5a66a0f..ffa475e 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableWriterImpl.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableWriterImpl.cs
@@ -61,17 +61,8 @@ namespace Apache.Ignite.Core.Impl.Portable
         /** Current raw position. */
         private long _curRawPos;
 
-        /** Current type structure. */
-        private PortableStructure _curStruct;
-
-        /** Current type structure path index. */
-        private int _curStructPath;
-
-        /** Current type structure action index. */
-        private int _curStructAction;
-
-        /** Current type structure updates. */
-        private List<PortableStructureUpdate> _curStructUpdates; 
+        /** Current type structure tracker, */
+        private PortableStructureTracker _curStruct;
         
         /** Whether we are currently detaching an object. */
         private bool _detaching;
@@ -1205,10 +1196,7 @@ namespace Apache.Ignite.Core.Impl.Portable
                 IPortableIdMapper oldMapper = _curMapper;
                 long oldRawPos = _curRawPos;
                 
-                PortableStructure oldStruct = _curStruct;
-                int oldStructPath = _curStructPath;
-                int oldStructAction = _curStructAction;
-                var oldStructUpdates = _curStructUpdates;
+                var oldStruct = _curStruct;
 
                 // Push new frame.
                 _curTypeId = desc.TypeId;
@@ -1216,10 +1204,7 @@ namespace Apache.Ignite.Core.Impl.Portable
                 _curMapper = desc.Mapper;
                 _curRawPos = 0;
 
-                _curStruct = desc.TypeStructure;
-                _curStructPath = 0;
-                _curStructAction = 0;
-                _curStructUpdates = null;
+                _curStruct = new PortableStructureTracker(desc, desc.WriterTypeStructure);
 
                 // Write object fields.
                 desc.Serializer.WritePortable(obj, this);
@@ -1235,23 +1220,7 @@ namespace Apache.Ignite.Core.Impl.Portable
                     _stream.WriteInt(pos + PU.OffsetRaw, len);
 
                 // Apply structure updates if any.
-                if (_curStructUpdates != null)
-                {
-                    desc.UpdateStructure(_curStruct, _curStructPath, _curStructUpdates);
-
-                    IPortableMetadataHandler metaHnd = _marsh.GetMetadataHandler(desc);
-
-                    if (metaHnd != null)
-                    {
-                        foreach (var u in _curStructUpdates)
-                            metaHnd.OnFieldWrite(u.FieldId, u.FieldName, u.FieldType);
-
-                        IDictionary<string, int> meta = metaHnd.OnObjectWriteFinished();
-
-                        if (meta != null)
-                            SaveMetadata(_curTypeId, desc.TypeName, desc.AffinityKeyFieldName, meta);
-                    }
-                }
+                _curStruct.UpdateWriterStructure(this);
 
                 // Restore old frame.
                 _curTypeId = oldTypeId;
@@ -1260,9 +1229,6 @@ namespace Apache.Ignite.Core.Impl.Portable
                 _curRawPos = oldRawPos;
 
                 _curStruct = oldStruct;
-                _curStructPath = oldStructPath;
-                _curStructAction = oldStructAction;
-                _curStructUpdates = oldStructUpdates;
             }
             else
             {
@@ -1519,40 +1485,7 @@ namespace Apache.Ignite.Core.Impl.Portable
             if (_curRawPos != 0)
                 throw new PortableException("Cannot write named fields after raw data is written.");
 
-            int action = _curStructAction++;
-
-            int fieldId;
-
-            if (_curStructUpdates == null)
-            {
-                fieldId = _curStruct.GetFieldId(fieldName, fieldTypeId, ref _curStructPath, action);
-
-                if (fieldId == 0)
-                    fieldId = GetNewFieldId(fieldName, fieldTypeId, action);
-            }
-            else
-                fieldId = GetNewFieldId(fieldName, fieldTypeId, action);
-
-            _stream.WriteInt(fieldId);
-        }
-
-        /// <summary>
-        /// Get ID for the new field and save structure update.
-        /// </summary>
-        /// <param name="fieldName">Field name.</param>
-        /// <param name="fieldTypeId">Field type ID.</param>
-        /// <param name="action">Action index.</param>
-        /// <returns>Field ID.</returns>
-        private int GetNewFieldId(string fieldName, byte fieldTypeId, int action)
-        {
-            int fieldId = PU.FieldId(_curTypeId, fieldName, _curConverter, _curMapper);
-
-            if (_curStructUpdates == null)
-                _curStructUpdates = new List<PortableStructureUpdate>();
-
-            _curStructUpdates.Add(new PortableStructureUpdate(fieldName, fieldId, fieldTypeId, action));
-
-            return fieldId;
+            _stream.WriteInt(_curStruct.GetFieldId(fieldName, fieldTypeId));
         }
 
         /// <summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructure.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructure.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructure.cs
index 2d47755..5b97ef7 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructure.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructure.cs
@@ -48,7 +48,7 @@ namespace Apache.Ignite.Core.Impl.Portable.Structure
         private readonly PortableStructureJumpTable[] _jumps;
 
         /** Field types. */
-        private readonly IDictionary<string, byte> _fieldTypes; 
+        private readonly IDictionary<string, byte> _fieldTypes;
 
         /// <summary>
         /// Constructor.
@@ -318,7 +318,7 @@ namespace Apache.Ignite.Core.Impl.Portable.Structure
                     newFieldTypes[update.FieldName] = update.FieldType;
             }
 
-            return newFieldTypes.Count == _fieldTypes.Count ? 
+            return newFieldTypes.Count == _fieldTypes.Count ?
                 this : new PortableStructure(_paths, _jumps, newFieldTypes);
         }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureEntry.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureEntry.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureEntry.cs
index 9476635..9b6d282 100644
--- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureEntry.cs
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureEntry.cs
@@ -34,7 +34,7 @@ namespace Apache.Ignite.Core.Impl.Portable.Structure
 
         /** Field type. */
         private readonly byte _type;
-        
+
         /// <summary>
         /// Constructor for jump table entry.
         /// </summary>

http://git-wip-us.apache.org/repos/asf/ignite/blob/f85c6a3e/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureTracker.cs
----------------------------------------------------------------------
diff --git a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureTracker.cs b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureTracker.cs
new file mode 100644
index 0000000..8cec87a
--- /dev/null
+++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/Structure/PortableStructureTracker.cs
@@ -0,0 +1,132 @@
+/*
+ * 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.
+ */
+
+namespace Apache.Ignite.Core.Impl.Portable.Structure
+{
+    using System.Collections.Generic;
+
+    /// <summary>
+    /// Encapsulates logic for tracking field access and updating type descriptor structure.
+    /// </summary>
+    internal struct PortableStructureTracker
+    {
+        /** Current type structure. */
+        private readonly IPortableTypeDescriptor _desc;
+
+        /** Struct. */
+        private readonly PortableStructure _portStruct;
+
+        /** Current type structure path index. */
+        private int _curStructPath;
+
+        /** Current type structure action index. */
+        private int _curStructAction;
+
+        /** Current type structure updates. */
+        private List<PortableStructureUpdate> _curStructUpdates;
+
+        /// <summary>
+        /// Initializes a new instance of the <see cref="PortableStructureTracker" /> class.
+        /// </summary>
+        /// <param name="desc">The desc.</param>
+        /// <param name="portStruct">The structure to work with.</param>
+        public PortableStructureTracker(IPortableTypeDescriptor desc, PortableStructure portStruct)
+        {
+            _desc = desc;
+            _portStruct = portStruct;
+            _curStructPath = 0;
+            _curStructAction = 0;
+            _curStructUpdates = null;
+        }
+
+        /// <summary>
+        /// Gets the field ID.
+        /// </summary>
+        public int GetFieldId(string fieldName, byte fieldTypeId = 0)
+        {
+            _curStructAction++;
+
+            if (_curStructUpdates == null)
+            {
+                var fieldId = _portStruct.GetFieldId(fieldName, fieldTypeId, ref _curStructPath,
+                    _curStructAction);
+
+                if (fieldId != 0)
+                    return fieldId;
+            }
+
+            return GetNewFieldId(fieldName, fieldTypeId, _curStructAction);
+        }
+
+        /// <summary>
+        /// Updates the type structure.
+        /// </summary>
+        public void UpdateReaderStructure()
+        {
+            if (_curStructUpdates != null)
+                _desc.UpdateReadStructure(_desc.ReaderTypeStructure, _curStructPath, _curStructUpdates);
+        }
+
+        /// <summary>
+        /// Updates the type structure and metadata for the specified writer.
+        /// </summary>
+        /// <param name="writer">The writer.</param>
+        public void UpdateWriterStructure(PortableWriterImpl writer)
+        {
+            if (_curStructUpdates != null)
+            {
+                _desc.UpdateWriteStructure(_desc.WriterTypeStructure, _curStructPath, _curStructUpdates);
+
+                var marsh = writer.Marshaller;
+
+                var metaHnd = marsh.GetMetadataHandler(_desc);
+
+                if (metaHnd != null)
+                {
+                    foreach (var u in _curStructUpdates)
+                        metaHnd.OnFieldWrite(u.FieldId, u.FieldName, u.FieldType);
+
+                    var meta = metaHnd.OnObjectWriteFinished();
+
+                    if (meta != null)
+                        writer.SaveMetadata(_desc.TypeId, _desc.TypeName, _desc.AffinityKeyFieldName, meta);
+                }
+            }
+        }
+
+        /// <summary>
+        /// Get ID for the new field and save structure update.
+        /// </summary>
+        /// <param name="fieldName">Field name.</param>
+        /// <param name="fieldTypeId">Field type ID.</param>
+        /// <param name="action">Action index.</param>
+        /// <returns>
+        /// Field ID.
+        /// </returns>
+        private int GetNewFieldId(string fieldName, byte fieldTypeId, int action)
+        {
+            var fieldId = PortableUtils.FieldId(_desc.TypeId, fieldName, _desc.NameConverter, _desc.Mapper);
+
+            if (_curStructUpdates == null)
+                _curStructUpdates = new List<PortableStructureUpdate>();
+
+            _curStructUpdates.Add(new PortableStructureUpdate(fieldName, fieldId, fieldTypeId, action));
+
+            return fieldId;
+        }
+    }
+}
\ No newline at end of file