Repository: ignite Updated Branches: refs/heads/ignite-1282 4e053e45d -> 1dbb37f02
IGNITE-1613: Optimized Guid read/write. Project: http://git-wip-us.apache.org/repos/asf/ignite/repo Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/1dbb37f0 Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/1dbb37f0 Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/1dbb37f0 Branch: refs/heads/ignite-1282 Commit: 1dbb37f02ac6f927e0887263d9f67eb20284a8e1 Parents: 4e053e4 Author: Pavel Tupitsyn <[email protected]> Authored: Wed Oct 7 15:22:58 2015 +0300 Committer: vozerov-gridgain <[email protected]> Committed: Wed Oct 7 15:22:58 2015 +0300 ---------------------------------------------------------------------- .../Apache.Ignite.Benchmarks.csproj | 1 + .../Apache.Ignite.Benchmarks/Model/TestModel.cs | 111 ++++++++++++++ .../Portable/PortableWriteBenchmark.cs | 56 ++++++- .../Portable/PortableApiSelfTest.cs | 2 +- .../Impl/Portable/PortableUtils.cs | 152 ++++++++++--------- 5 files changed, 245 insertions(+), 77 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/ignite/blob/1dbb37f0/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 19f2724..a85b189 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Apache.Ignite.Benchmarks.csproj +++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Apache.Ignite.Benchmarks.csproj @@ -56,6 +56,7 @@ <Compile Include="Model\Department.cs" /> <Compile Include="Model\Employee.cs" /> <Compile Include="Model\Sex.cs" /> + <Compile Include="Model\TestModel.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/1dbb37f0/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Model/TestModel.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Model/TestModel.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Model/TestModel.cs new file mode 100644 index 0000000..1c87018 --- /dev/null +++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Model/TestModel.cs @@ -0,0 +1,111 @@ +/* + * 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.Model +{ + using System; + using Apache.Ignite.Core.Portable; + + /// <summary> + /// Model class with all kinds of fields to test serialization. + /// </summary> + internal class TestModel : IPortableMarshalAware + { + public byte Byte { get; set; } + public byte[] ByteArray { get; set; } + public char Char { get; set; } + public char[] CharArray { get; set; } + public short Short { get; set; } + public short[] ShortArray { get; set; } + public int Int { get; set; } + public int[] IntArray { get; set; } + public long Long { get; set; } + public long[] LongArray { get; set; } + public bool Boolean { get; set; } + public bool[] BooleanArray { get; set; } + public float Float { get; set; } + public float[] FloatArray { get; set; } + public double Double { get; set; } + public double[] DoubleArray { get; set; } + public decimal Decimal { get; set; } + public decimal[] DecimalArray { get; set; } + public DateTime? Date { get; set; } + public DateTime?[] DateArray { get; set; } + public string String { get; set; } + public string[] StringArray { get; set; } + public Guid? Guid { get; set; } + public Guid?[] GuidArray { get; set; } + + /** <inheritDoc /> */ + public void WritePortable(IPortableWriter writer) + { + writer.WriteByte("Byte", Byte); + writer.WriteByteArray("ByteArray", ByteArray); + writer.WriteChar("Char", Char); + writer.WriteCharArray("CharArray", CharArray); + writer.WriteShort("Short", Short); + writer.WriteShortArray("ShortArray", ShortArray); + writer.WriteInt("Int", Int); + writer.WriteIntArray("IntArray", IntArray); + writer.WriteLong("Long", Long); + writer.WriteLongArray("LongArray", LongArray); + writer.WriteBoolean("Boolean", Boolean); + writer.WriteBooleanArray("BooleanArray", BooleanArray); + writer.WriteFloat("Float", Float); + writer.WriteFloatArray("FloatArray", FloatArray); + writer.WriteDouble("Double", Double); + writer.WriteDoubleArray("DoubleArray", DoubleArray); + writer.WriteDecimal("Decimal", Decimal); + writer.WriteDecimalArray("DecimalArray", DecimalArray); + writer.WriteDate("Date", Date); + writer.WriteDateArray("DateArray", DateArray); + writer.WriteString("String", String); + writer.WriteStringArray("StringArray", StringArray); + writer.WriteGuid("Guid", Guid); + writer.WriteGuidArray("GuidArray", GuidArray); + } + + /** <inheritDoc /> */ + public void ReadPortable(IPortableReader reader) + { + Byte = reader.ReadByte("Byte"); + ByteArray = reader.ReadByteArray("ByteArray"); + Char = reader.ReadChar("Char"); + CharArray = reader.ReadCharArray("CharArray"); + Short = reader.ReadShort("Short"); + ShortArray = reader.ReadShortArray("ShortArray"); + Int = reader.ReadInt("Int"); + IntArray = reader.ReadIntArray("IntArray"); + Long = reader.ReadLong("Long"); + LongArray = reader.ReadLongArray("LongArray"); + Boolean = reader.ReadBoolean("Boolean"); + BooleanArray = reader.ReadBooleanArray("BooleanArray"); + Float = reader.ReadFloat("Float"); + FloatArray = reader.ReadFloatArray("FloatArray"); + Double = reader.ReadDouble("Double"); + DoubleArray = reader.ReadDoubleArray("DoubleArray"); + Decimal = reader.ReadDecimal("Decimal"); + DecimalArray = reader.ReadDecimalArray("DecimalArray"); + Date = reader.ReadDate("Date"); + DateArray = reader.ReadDateArray("DateArray"); + String = reader.ReadString("String"); + StringArray = reader.ReadStringArray("StringArray"); + Guid = reader.ReadGuid("Guid"); + GuidArray = reader.ReadGuidArray("GuidArray"); + } + } +} http://git-wip-us.apache.org/repos/asf/ignite/blob/1dbb37f0/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableWriteBenchmark.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableWriteBenchmark.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableWriteBenchmark.cs index 5638195..7ac27ea 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableWriteBenchmark.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Portable/PortableWriteBenchmark.cs @@ -17,7 +17,9 @@ 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; @@ -34,9 +36,38 @@ namespace Apache.Ignite.Benchmarks.Portable /** Memory manager. */ private readonly PlatformMemoryManager _memMgr = new PlatformMemoryManager(1024); - /** Pre-allocated addess. */ + /** 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[] {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="PortableWriteBenchmark"/> class. /// </summary> @@ -46,7 +77,8 @@ namespace Apache.Ignite.Benchmarks.Portable { TypeConfigurations = new List<PortableTypeConfiguration> { - new PortableTypeConfiguration(typeof (Address)) {MetadataEnabled = true} + new PortableTypeConfiguration(typeof (Address)) {MetadataEnabled = true}, + new PortableTypeConfiguration(typeof (TestModel)) {MetadataEnabled = false} } }); } @@ -58,6 +90,7 @@ namespace Apache.Ignite.Benchmarks.Portable protected override void GetDescriptors(ICollection<BenchmarkOperationDescriptor> descs) { descs.Add(BenchmarkOperationDescriptor.Create("WriteAddress", WriteAddress, 1)); + descs.Add(BenchmarkOperationDescriptor.Create("WriteTestModel", WriteTestModel, 1)); } /// <summary> @@ -79,5 +112,24 @@ namespace Apache.Ignite.Benchmarks.Portable mem.Release(); } } + /// <summary> + /// Write address. + /// </summary> + /// <param name="state">State.</param> + private void WriteTestModel(BenchmarkState state) + { + var mem = _memMgr.Allocate(); + + try + { + var stream = mem.GetStream(); + + _marsh.StartMarshal(stream).Write(_model); + } + finally + { + mem.Release(); + } + } } } http://git-wip-us.apache.org/repos/asf/ignite/blob/1dbb37f0/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableApiSelfTest.cs ---------------------------------------------------------------------- diff --git a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableApiSelfTest.cs b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableApiSelfTest.cs index b9a9936..8c5bb0b 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableApiSelfTest.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core.Tests/Portable/PortableApiSelfTest.cs @@ -1381,7 +1381,7 @@ namespace Apache.Ignite.Core.Tests.Portable Assert.AreEqual(IdMapper.TestTypeId, _grid.GetPortables().GetTypeId(IdMapper.TestTypeName)); - Assert.AreEqual(PortableUtils.StringHashCode("someTypeName"), _grid.GetPortables().GetTypeId("someTypeName")); + Assert.AreEqual(PortableUtils.GetStringHashCode("someTypeName"), _grid.GetPortables().GetTypeId("someTypeName")); } /// <summary> http://git-wip-us.apache.org/repos/asf/ignite/blob/1dbb37f0/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 fb0b195..d02fcf1 100644 --- a/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableUtils.cs +++ b/modules/platforms/dotnet/Apache.Ignite.Core/Impl/Portable/PortableUtils.cs @@ -25,6 +25,7 @@ namespace Apache.Ignite.Core.Impl.Portable using System.Diagnostics.CodeAnalysis; using System.IO; using System.Reflection; + using System.Runtime.InteropServices; using System.Runtime.Serialization.Formatters.Binary; using System.Text; using Apache.Ignite.Core.Impl.Common; @@ -992,29 +993,13 @@ namespace Apache.Ignite.Core.Impl.Portable */ public static unsafe void WriteGuid(Guid val, IPortableStream stream) { - byte[] bytes = val.ToByteArray(); + var jguid = new JavaGuid(val); - // .Net returns bytes in the following order: _a(4), _b(2), _c(2), _d, _e, _g, _h, _i, _j, _k. - // And _a, _b and _c are always in little endian format irrespective of system configuration. - // To be compliant with Java we rearrange them as follows: _c, _b_, a_, _k, _j, _i, _h, _g, _e, _d. - fixed (byte* bytes0 = bytes) - { - stream.Write(bytes0 + 6, 2); // _c - stream.Write(bytes0 + 4, 2); // _a - stream.Write(bytes0, 4); // _a - } - - stream.WriteByte(bytes[15]); // _k - stream.WriteByte(bytes[14]); // _j - stream.WriteByte(bytes[13]); // _i - stream.WriteByte(bytes[12]); // _h + var ptr = &jguid; - stream.WriteByte(bytes[11]); // _g - stream.WriteByte(bytes[10]); // _f - stream.WriteByte(bytes[9]); // _e - stream.WriteByte(bytes[8]); // _d + stream.Write((byte*) ptr, 16); } - + /** * <summary>Read GUID.</summary> * <param name="stream">Stream.</param> @@ -1022,61 +1007,15 @@ namespace Apache.Ignite.Core.Impl.Portable */ public static unsafe Guid? ReadGuid(IPortableStream stream) { - byte[] bytes = new byte[16]; - - // Perform conversion opposite to what write does. - fixed (byte* bytes0 = bytes) - { - stream.Read(bytes0 + 6, 2); // _c - stream.Read(bytes0 + 4, 2); // _b - stream.Read(bytes0, 4); // _a - } - - bytes[15] = stream.ReadByte(); // _k - bytes[14] = stream.ReadByte(); // _j - bytes[13] = stream.ReadByte(); // _i - bytes[12] = stream.ReadByte(); // _h - - bytes[11] = stream.ReadByte(); // _g - bytes[10] = stream.ReadByte(); // _f - bytes[9] = stream.ReadByte(); // _e - bytes[8] = stream.ReadByte(); // _d - - return new Guid(bytes); - } + JavaGuid jguid; - /** - * <summary>Read GUID.</summary> - * <param name="data">Data array.</param> - * <param name="pos">Position.</param> - * <returns>GUID</returns> - */ - public static Guid ReadGuid(byte[] data, int pos) { - byte[] bytes = new byte[16]; + var ptr = (byte*) &jguid; - // Perform conversion opposite to what write does. - bytes[6] = data[pos]; // _c - bytes[7] = data[pos + 1]; + stream.Read(ptr, 16); - bytes[4] = data[pos + 2]; // _b - bytes[5] = data[pos + 3]; + var dotnetGuid = new GuidAccessor(jguid); - bytes[0] = data[pos + 4]; // _a - bytes[1] = data[pos + 5]; - bytes[2] = data[pos + 6]; - bytes[3] = data[pos + 7]; - - bytes[15] = data[pos + 8]; // _k - bytes[14] = data[pos + 9]; // _j - bytes[13] = data[pos + 10]; // _i - bytes[12] = data[pos + 11]; // _h - - bytes[11] = data[pos + 12]; // _g - bytes[10] = data[pos + 13]; // _f - bytes[9] = data[pos + 14]; // _e - bytes[8] = data[pos + 15]; // _d - - return new Guid(bytes); + return *(Guid*) (&dotnetGuid); } /// <summary> @@ -1590,7 +1529,7 @@ namespace Apache.Ignite.Core.Impl.Portable * <param name="val">Value.</param> * <returns>Hash code.</returns> */ - public static int StringHashCode(string val) + public static int GetStringHashCode(string val) { if (val == null) return 0; @@ -1761,7 +1700,7 @@ namespace Apache.Ignite.Core.Impl.Portable } if (id == 0) - id = StringHashCode(typeName); + id = GetStringHashCode(typeName); return id; } @@ -1797,7 +1736,7 @@ namespace Apache.Ignite.Core.Impl.Portable } if (id == 0) - id = StringHashCode(fieldName); + id = GetStringHashCode(fieldName); if (id == 0) throw new PortableException("Field ID is zero (please provide ID mapper or change field name) " + @@ -2183,5 +2122,70 @@ namespace Apache.Ignite.Core.Impl.Portable throw new PortableException("Failed to find class: " + typeName); } + + /// <summary> + /// Reverses the byte order of an unsigned long. + /// </summary> + private static ulong ReverseByteOrder(ulong l) + { + // Fastest way would be to use bswap processor instruction. + return ((l >> 56) & 0x00000000000000FF) | ((l >> 40) & 0x000000000000FF00) | + ((l >> 24) & 0x0000000000FF0000) | ((l >> 8) & 0x00000000FF000000) | + ((l << 8) & 0x000000FF00000000) | ((l << 24) & 0x0000FF0000000000) | + ((l << 40) & 0x00FF000000000000) | ((l << 56) & 0xFF00000000000000); + } + + /// <summary> + /// Struct with .Net-style Guid memory layout. + /// </summary> + [StructLayout(LayoutKind.Sequential)] + private struct GuidAccessor + { + public readonly ulong ABC; + public readonly ulong DEGHIJK; + + /// <summary> + /// Initializes a new instance of the <see cref="GuidAccessor"/> struct. + /// </summary> + /// <param name="val">The value.</param> + public GuidAccessor(JavaGuid val) + { + var l = val.CBA; + + ABC = ((l >> 32) & 0x00000000FFFFFFFF) | ((l << 48) & 0xFFFF000000000000) | + ((l << 16) & 0x0000FFFF00000000); + + DEGHIJK = ReverseByteOrder(val.KJIHGED); + } + } + + /// <summary> + /// Struct with Java-style Guid memory layout. + /// </summary> + [StructLayout(LayoutKind.Sequential)] + private struct JavaGuid + { + public readonly ulong CBA; + public readonly ulong KJIHGED; + + /// <summary> + /// Initializes a new instance of the <see cref="JavaGuid"/> struct. + /// </summary> + /// <param name="val">The value.</param> + public unsafe JavaGuid(Guid val) + { + // .Net returns bytes in the following order: _a(4), _b(2), _c(2), _d, _e, _g, _h, _i, _j, _k. + // And _a, _b and _c are always in little endian format irrespective of system configuration. + // To be compliant with Java we rearrange them as follows: _c, _b_, a_, _k, _j, _i, _h, _g, _e, _d. + var accessor = *((GuidAccessor*)&val); + + var l = accessor.ABC; + + CBA = ((l << 32) & 0xFFFFFFFF00000000) | ((l >> 48) & 0x000000000000FFFF) | + ((l >> 16) & 0x00000000FFFF0000); + + KJIHGED = ReverseByteOrder(accessor.DEGHIJK); + } + } } }
