IGNITE-7530 .NET: Fix GetAll memory usage and performance in binary mode
authorPavel Tupitsyn <ptupitsyn@apache.org>
Fri, 26 Jan 2018 08:48:14 +0000 (11:48 +0300)
committerPavel Tupitsyn <ptupitsyn@apache.org>
Fri, 26 Jan 2018 08:48:14 +0000 (11:48 +0300)
This closes #3436

12 files changed:
modules/platforms/dotnet/Apache.Ignite.Benchmarks/Apache.Ignite.Benchmarks.csproj
modules/platforms/dotnet/Apache.Ignite.Benchmarks/BenchmarkRunner.cs
modules/platforms/dotnet/Apache.Ignite.Benchmarks/Interop/GetAllBenchmark.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Benchmarks/Interop/GetAllBinaryBenchmark.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Benchmarks/Model/Doubles.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Benchmarks/ThinClient/ThinClientGetAllBenchmark.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Benchmarks/ThinClient/ThinClientGetAllBinaryBenchmark.cs [new file with mode: 0644]
modules/platforms/dotnet/Apache.Ignite.Core.Tests/Binary/IO/BinaryStreamsTest.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/BinaryReader.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/BinaryHeapStream.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Binary/Io/IBinaryStream.cs
modules/platforms/dotnet/Apache.Ignite.Core/Impl/Memory/PlatformMemoryStream.cs

index e712337..1c200e6 100644 (file)
@@ -49,6 +49,8 @@
     <Compile Include="BenchmarkRunner.cs" />
     <Compile Include="BenchmarkState.cs" />
     <Compile Include="BenchmarkUtils.cs" />
+    <Compile Include="Interop\GetAllBinaryBenchmark.cs" />
+    <Compile Include="Interop\GetAllBenchmark.cs" />
     <Compile Include="Interop\PlatformBenchmarkBase.cs" />
     <Compile Include="Interop\ClosureBenchmark.cs" />
     <Compile Include="Interop\GetAsyncBenchmark.cs" />
@@ -60,6 +62,7 @@
     <Compile Include="Model\Address.cs" />
     <Compile Include="Model\Company.cs" />
     <Compile Include="Model\Department.cs" />
+    <Compile Include="Model\Doubles.cs" />
     <Compile Include="Model\Employee.cs" />
     <Compile Include="Model\Sex.cs" />
     <Compile Include="Model\TestModel.cs" />
@@ -69,7 +72,9 @@
     <Compile Include="Result\BenchmarkConsoleResultWriter.cs" />
     <Compile Include="Result\BenchmarkFileResultWriter.cs" />
     <Compile Include="Result\IBenchmarkResultWriter.cs" />
+    <Compile Include="ThinClient\ThinClientGetAllBinaryBenchmark.cs" />
     <Compile Include="ThinClient\ThinClientGetAsyncBenchmark.cs" />
+    <Compile Include="ThinClient\ThinClientGetAllBenchmark.cs" />
     <Compile Include="ThinClient\ThinClientGetBenchmark.cs" />
     <Compile Include="ThinClient\ThinClientPutAsyncBenchmark.cs" />
     <Compile Include="ThinClient\ThinClientPutBenchmark.cs" />
index 9d86da2..e152ffb 100644 (file)
@@ -19,6 +19,7 @@ namespace Apache.Ignite.Benchmarks
 {
     using System;
     using System.Diagnostics;
+    using System.IO;
     using System.Text;
     using Apache.Ignite.Benchmarks.Interop;
     using Apache.Ignite.Benchmarks.ThinClient;
@@ -35,13 +36,16 @@ namespace Apache.Ignite.Benchmarks
         // ReSharper disable once RedundantAssignment
         public static void Main(string[] args)
         {
-            args = new[] { 
-                typeof(ThinClientGetBenchmark).FullName,
-                "-ConfigPath", @"S:\W\incubator-ignite\modules\platforms\dotnet\Apache.Ignite.Benchmarks\Config\benchmark.xml",
+            args = new[] {
+                //typeof(GetAllBenchmark).FullName,
+                typeof(GetAllBinaryBenchmark).FullName,
+                //typeof(ThinClientGetAllBenchmark).FullName,
+                //typeof(ThinClientGetAllBinaryBenchmark).FullName,
+                "-ConfigPath", Directory.GetCurrentDirectory() + @"\..\..\Config\benchmark.xml",
                 "-Threads", "1",
                 "-Warmup", "0",
                 "-Duration", "60",
-                "-BatchSize", "1000"
+                "-BatchSize", "1"
             };
 
             var gcSrv = System.Runtime.GCSettings.IsServerGC;
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Interop/GetAllBenchmark.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Interop/GetAllBenchmark.cs
new file mode 100644 (file)
index 0000000..07ca269
--- /dev/null
@@ -0,0 +1,71 @@
+/*
+ * 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.Interop
+{
+    using System.Collections.Generic;
+    using System.Linq;
+    using Apache.Ignite.Benchmarks.Model;
+    using Apache.Ignite.Core.Cache;
+
+    /// <summary>
+    /// Cache GetAll benchmark.
+    /// </summary>
+    internal class GetAllBenchmark : PlatformBenchmarkBase
+    {
+        /** Cache name. */
+        private const string CacheName = "doubles";
+
+        /** Native cache wrapper. */
+        protected ICache<int, Doubles> Cache;
+
+        /** GetAll batch size */
+        protected const int DatasetBatchSize = 100;
+
+        /** <inheritDoc /> */
+        protected override void OnStarted()
+        {
+            Dataset = 1000;
+
+            base.OnStarted();
+
+            Cache = Node.GetOrCreateCache<int, Doubles>(CacheName);
+
+            var array = Doubles.GetInstances(Dataset);
+
+            for (int i = 0; i < array.Length; i++)
+                Cache.Put(i, array[i]);
+        }
+
+        /** <inheritDoc /> */
+        protected override void GetDescriptors(ICollection<BenchmarkOperationDescriptor> descs)
+        {
+            descs.Add(BenchmarkOperationDescriptor.Create("GetAll", GetAll, 1));
+        }
+
+        /// <summary>
+        /// Cache GetAll.
+        /// </summary>
+        private void GetAll(BenchmarkState state)
+        {
+            var idx = BenchmarkUtils.GetRandomInt(Dataset - DatasetBatchSize);
+            var keys = Enumerable.Range(idx, DatasetBatchSize);
+
+            Cache.GetAll(keys);
+        }
+    }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Interop/GetAllBinaryBenchmark.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Interop/GetAllBinaryBenchmark.cs
new file mode 100644 (file)
index 0000000..87e7cee
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.Interop
+{
+    using System.Collections.Generic;
+    using System.Linq;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Cache;
+
+    /// <summary>
+    /// Cache GetAll binary benchmark.
+    /// </summary>
+    internal class GetAllBinaryBenchmark : GetAllBenchmark
+    {
+        /** Binary cache wrapper. */
+        private ICache<int, IBinaryObject> _binaryCache;
+
+        /** <inheritDoc /> */
+        protected override void OnStarted()
+        {
+            base.OnStarted();
+
+            _binaryCache = Cache.WithKeepBinary<int, IBinaryObject>();
+        }
+
+        /** <inheritDoc /> */
+        protected override void GetDescriptors(ICollection<BenchmarkOperationDescriptor> descs)
+        {
+            descs.Add(BenchmarkOperationDescriptor.Create("GetAllBinary", GetAllBinary, 1));
+        }
+
+        /// <summary>
+        /// Cache GetAll.
+        /// </summary>
+        private void GetAllBinary(BenchmarkState state)
+        {
+            var idx = BenchmarkUtils.GetRandomInt(Dataset - DatasetBatchSize);
+            var keys = Enumerable.Range(idx, DatasetBatchSize);
+
+            _binaryCache.GetAll(keys);
+        }
+    }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Model/Doubles.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/Model/Doubles.cs
new file mode 100644 (file)
index 0000000..0b01715
--- /dev/null
@@ -0,0 +1,69 @@
+/*
+ * 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.Binary;
+
+    /// <summary>
+    /// Model with a single double[] field.
+    /// </summary>
+    public class Doubles : IBinarizable
+    {
+        /** */
+        private double[] _data = new double[1000 * 32];
+
+        /// <summary>
+        /// Initializes the instance.
+        /// </summary>
+        private Doubles Init(Random random)
+        {
+            for (var i = 0; i < _data.Length; i++)
+            {
+                _data[i] = random.NextDouble();
+            }
+            return this;
+        }
+
+        /// <summary>
+        /// Gets the instances.
+        /// </summary>
+        public static Doubles[] GetInstances(int size)
+        {
+            var data = new Doubles[size];
+            var random = new Random();
+            for (var i = 0; i < data.Length; i++)
+            {
+                data[i] = new Doubles().Init(random);
+            }
+            return data;
+        }
+
+        /** <inheritdoc /> */
+        public void WriteBinary(IBinaryWriter writer)
+        {
+            writer.WriteDoubleArray("data", _data);
+        }
+
+        /** <inheritdoc /> */
+        public void ReadBinary(IBinaryReader reader)
+        {
+            _data = reader.ReadDoubleArray("data");
+        }
+    }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/ThinClient/ThinClientGetAllBenchmark.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/ThinClient/ThinClientGetAllBenchmark.cs
new file mode 100644 (file)
index 0000000..d77a8a5
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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.ThinClient
+{
+    using System.Collections.Generic;
+    using System.Linq;
+    using Apache.Ignite.Benchmarks.Interop;
+    using Apache.Ignite.Benchmarks.Model;
+    using Apache.Ignite.Core.Client.Cache;
+
+    /// <summary>
+    /// Cache GetAll benchmark.
+    /// </summary>
+    internal class ThinClientGetAllBenchmark : PlatformBenchmarkBase
+    {
+        /** Cache name. */
+        private const string CacheName = "doubles";
+
+        /** Native cache wrapper. */
+        protected ICacheClient<int, Doubles> Cache;
+
+        /** GetAll batch size */
+        protected const int DatasetBatchSize = 100;
+
+        /** <inheritDoc /> */
+        protected override void OnStarted()
+        {
+            Dataset = 1000;
+
+            base.OnStarted();
+
+            Cache = GetClient().GetOrCreateCache<int, Doubles>(CacheName);
+
+            var array = Doubles.GetInstances(Dataset);
+
+            for (int i = 0; i < array.Length; i++)
+                Cache.Put(i, array[i]);
+        }
+
+        /** <inheritDoc /> */
+        protected override void GetDescriptors(ICollection<BenchmarkOperationDescriptor> descs)
+        {
+            descs.Add(BenchmarkOperationDescriptor.Create("ThinClientGetAll", GetAll, 1));
+        }
+
+        /// <summary>
+        /// Cache GetAll.
+        /// </summary>
+        private void GetAll(BenchmarkState state)
+        {
+            var idx = BenchmarkUtils.GetRandomInt(Dataset - DatasetBatchSize);
+            var keys = Enumerable.Range(idx, DatasetBatchSize);
+
+            Cache.GetAll(keys);
+        }
+    }
+}
diff --git a/modules/platforms/dotnet/Apache.Ignite.Benchmarks/ThinClient/ThinClientGetAllBinaryBenchmark.cs b/modules/platforms/dotnet/Apache.Ignite.Benchmarks/ThinClient/ThinClientGetAllBinaryBenchmark.cs
new file mode 100644 (file)
index 0000000..28fa79e
--- /dev/null
@@ -0,0 +1,58 @@
+/*
+ * 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.ThinClient
+{
+    using System.Collections.Generic;
+    using System.Linq;
+    using Apache.Ignite.Core.Binary;
+    using Apache.Ignite.Core.Client.Cache;
+
+    /// <summary>
+    /// Cache GetAll binary benchmark.
+    /// </summary>
+    internal class ThinClientGetAllBinaryBenchmark : ThinClientGetAllBenchmark
+    {
+        /** Binary cache wrapper. */
+        private ICacheClient<int, IBinaryObject> _binaryCache;
+
+        /** <inheritDoc /> */
+        protected override void OnStarted()
+        {
+            base.OnStarted();
+
+            _binaryCache = Cache.WithKeepBinary<int, IBinaryObject>();
+        }
+
+        /** <inheritDoc /> */
+        protected override void GetDescriptors(ICollection<BenchmarkOperationDescriptor> descs)
+        {
+            descs.Add(BenchmarkOperationDescriptor.Create("ThinClientGetAll.Binary", GetAllBinary, 1));
+        }
+
+        /// <summary>
+        /// Cache GetAll.
+        /// </summary>
+        private void GetAllBinary(BenchmarkState state)
+        {
+            var idx = BenchmarkUtils.GetRandomInt(Dataset - DatasetBatchSize);
+            var keys = Enumerable.Range(idx, DatasetBatchSize);
+
+            _binaryCache.GetAll(keys);
+        }
+    }
+}
index 1ebe906..a1c045f 100644 (file)
@@ -83,7 +83,10 @@ namespace Apache.Ignite.Core.Tests.Binary.IO
             };
 
             // Arrays.
-            Assert.AreEqual(sameArr, stream.IsSameArray(stream.GetArray()));
+            if (stream.CanGetArray)
+            {
+                Assert.AreEqual(sameArr, stream.IsSameArray(stream.GetArray()));
+            }
             Assert.IsFalse(stream.IsSameArray(new byte[1]));
             Assert.IsFalse(stream.IsSameArray(stream.GetArrayCopy()));
 
index c0fcc7f..d35a45d 100644 (file)
@@ -656,8 +656,10 @@ namespace Apache.Ignite.Core.Impl.Binary
 
                 var hdr = BinaryObjectHeader.Read(Stream, pos);
 
-                if (!doDetach)
+                if (!doDetach && Stream.CanGetArray)
+                {
                     return new BinaryObject(_marsh, Stream.GetArray(), pos, hdr);
+                }
 
                 Stream.Seek(pos, SeekOrigin.Begin);
 
@@ -698,14 +700,16 @@ namespace Apache.Ignite.Core.Impl.Binary
                 {
                     BinaryObject portObj;
 
-                    if (_detach)
+                    if (_detach || !Stream.CanGetArray)
                     {
                         Stream.Seek(pos, SeekOrigin.Begin);
 
                         portObj = new BinaryObject(_marsh, Stream.ReadByteArray(hdr.Length), 0, hdr);
                     }
                     else
+                    {
                         portObj = new BinaryObject(_marsh, Stream.GetArray(), pos, hdr);
+                    }
 
                     T obj = _builder == null ? TypeCaster<T>.Cast(portObj) : TypeCaster<T>.Cast(_builder.Child(portObj));
 
index 6bd03fd..f104f6c 100644 (file)
@@ -1309,6 +1309,12 @@ namespace Apache.Ignite.Core.Impl.Binary.IO
         }
 
         /** <inheritdoc /> */
+        public bool CanGetArray
+        {
+            get { return true; }
+        }
+
+        /** <inheritdoc /> */
         public byte[] GetArrayCopy()
         {
             byte[] copy = new byte[_pos];
index 09d0be7..32bca5e 100644 (file)
@@ -291,12 +291,17 @@ namespace Apache.Ignite.Core.Impl.Binary.IO
         int Remaining { get; }
 
         /// <summary>
-        /// Gets underlying array, avoiding copying if possible.
+        /// Gets underlying array, avoiding copying.
         /// </summary>
         /// <returns>Underlying array.</returns>
         byte[] GetArray();
 
         /// <summary>
+        /// Gets a value indicating whether this instance can return underlying array without copying.
+        /// </summary>
+        bool CanGetArray { get; }
+
+        /// <summary>
         /// Gets underlying data in a new array.
         /// </summary>
         /// <returns>New array with data.</returns>
index 033de7e..5430e6a 100644 (file)
@@ -921,9 +921,17 @@ namespace Apache.Ignite.Core.Impl.Memory
         /// </returns>
         public byte[] GetArray()
         {
-            return GetArrayCopy();
+            throw new NotSupportedException("Off-heap stream can not return array without copy.");
         }
-        
+
+        /// <summary>
+        /// Gets a value indicating whether this instance can return underlying array without copying.
+        /// </summary>
+        public bool CanGetArray
+        {
+            get { return false; }
+        }
+
         /// <summary>
         /// Gets underlying data in a new array.
         /// </summary>