THRIFT-4879 general performance improvements for netstd library
Client: netstd
Patch: Jens Geyer

This closes #1808
diff --git a/test/netstd/Client/Performance/PerformanceTests.cs b/test/netstd/Client/Performance/PerformanceTests.cs
new file mode 100644
index 0000000..041d12e
--- /dev/null
+++ b/test/netstd/Client/Performance/PerformanceTests.cs
@@ -0,0 +1,150 @@
+// 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.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using ThriftTest;
+using Thrift.Collections;
+using Thrift.Protocol;
+using System.Threading;
+using Thrift.Transport.Client;
+using System.Threading.Tasks;
+using System.Diagnostics;
+using Thrift.Transport;
+
+namespace Client.Tests
+{
+    public class PerformanceTests
+    {
+        private CancellationTokenSource Cancel;
+        private CrazyNesting Testdata;
+        private TMemoryBufferTransport MemBuffer;
+        private TTransport Transport;
+        private LayeredChoice Layered;
+
+        internal static int Execute()
+        {
+            var instance = new PerformanceTests();
+            instance.ProtocolPeformanceTestAsync().Wait();
+
+            // debug only
+            if (Debugger.IsAttached)
+            {
+                Console.Write("Hit ENTER ...");
+                Console.ReadKey();
+            }
+
+            return 0;
+        }
+
+        private async Task ProtocolPeformanceTestAsync()
+        {
+            Console.WriteLine("Setting up for ProtocolPeformanceTestAsync ...");
+            Cancel = new CancellationTokenSource();
+            Testdata = TestDataFactory.CreateCrazyNesting();
+
+            foreach (var layered in Enum.GetValues(typeof(LayeredChoice)))
+            {
+                Layered = (LayeredChoice)layered;
+
+                await RunTestAsync(async (bool b) => { return await GenericProtocolFactory<TBinaryProtocol>(b); });
+                await RunTestAsync(async (bool b) => { return await GenericProtocolFactory<TCompactProtocol>(b); });
+                //await RunTestAsync(async (bool b) => { return await GenericProtocolFactory<TJsonProtocol>(b); });
+            }
+        }
+
+        private Task<TProtocol> GenericProtocolFactory<T>(bool forWrite)
+            where T : TProtocol
+        {
+            var oldTrans = Transport;
+            try
+            {
+                // read happens after write here, so let's take over the written bytes
+                if (forWrite)
+                    MemBuffer = new TMemoryBufferTransport();  
+                else
+                    MemBuffer = new TMemoryBufferTransport(MemBuffer.GetBuffer());
+
+                //  layered transports anyone?
+                switch (Layered)
+                {
+                    case LayeredChoice.None:
+                        Transport = MemBuffer;
+                        break;
+                    case LayeredChoice.Framed:
+                        Transport = new TFramedTransport(MemBuffer);
+                        break;
+                    case LayeredChoice.Buffered:
+                        Transport = new TBufferedTransport(MemBuffer);
+                        break;
+                    default:
+                        Debug.Assert(false);
+                        break;
+                }
+
+                if (!Transport.IsOpen)
+                    Transport.OpenAsync().Wait();
+
+                var instance = (T)Activator.CreateInstance(typeof(T), Transport);
+                return Task.FromResult<TProtocol>(instance);
+            }
+            finally
+            {
+                if (oldTrans is IDisposable)
+                    (oldTrans as IDisposable).Dispose();
+            }
+        }
+
+        private string GetProtocolTransportName(TProtocol proto)
+        {
+            var name = Transport.GetType().Name;
+            if (name.Equals(MemBuffer.GetType().Name))
+                name = string.Empty;
+            else
+                name = " + " + name;
+
+            name = proto.GetType().Name + name;
+            return name;
+        }
+
+
+        private async Task RunTestAsync(Func<bool, Task<TProtocol>> factory)
+        {
+            var stop = new Stopwatch();
+
+            var proto = await factory(true);
+            stop.Start();
+            await Testdata.WriteAsync(proto, Cancel.Token);
+            await Transport.FlushAsync(Cancel.Token);
+            stop.Stop();
+            Console.WriteLine("RunTestAsync({0}): write = {1} msec",
+                GetProtocolTransportName(proto),
+                stop.ElapsedMilliseconds);
+
+            var restored = new CrazyNesting();
+            proto = await factory(false);
+            stop.Start();
+            await restored.ReadAsync(proto, Cancel.Token);
+            stop.Stop();
+            Console.WriteLine("RunTestAsync({0}): read = {1} msec",
+                GetProtocolTransportName(proto),
+                stop.ElapsedMilliseconds);
+        }
+
+    }
+}
diff --git a/test/netstd/Client/Performance/TestDataFactory.cs b/test/netstd/Client/Performance/TestDataFactory.cs
new file mode 100644
index 0000000..9896285
--- /dev/null
+++ b/test/netstd/Client/Performance/TestDataFactory.cs
@@ -0,0 +1,154 @@
+// 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.
+
+using System;
+using System.Collections.Generic;
+using System.Text;
+using ThriftTest;
+using Thrift.Collections;
+
+namespace Client.Tests
+{
+    
+    static class TestDataFactory
+    {
+        public static CrazyNesting CreateCrazyNesting(int count = 10)
+        {
+            if (count <= 0)
+                return null;
+
+            return new CrazyNesting()
+            {
+                Binary_field = CreateBytesArray(count),
+                List_field = CreateListField(count),
+                Set_field = CreateSetField(count),
+                String_field = string.Format("data level {0}", count)
+            };
+        }
+
+        private static THashSet<Insanity> CreateSetField(int count)
+        {
+            var retval = new THashSet<Insanity>();
+            for (var i = 0; i < count; ++i)
+                retval.Add(CreateInsanity(count));
+            return retval;
+        }
+
+        private static Insanity CreateInsanity(int count)
+        {
+            return new Insanity()
+            {
+                UserMap = CreateUserMap(count),
+                Xtructs = CreateXtructs(count)
+            };
+        }
+
+        private static List<Xtruct> CreateXtructs(int count)
+        {
+            var retval = new List<Xtruct>();
+            for (var i = 0; i < count; ++i)
+                retval.Add(CreateXtruct(count));
+            return retval;
+        }
+
+        private static Xtruct CreateXtruct(int count)
+        {
+            return new Xtruct()
+            {
+                Byte_thing = (sbyte)(count % 128),
+                I32_thing = count,
+                I64_thing = count,
+                String_thing = string.Format("data level {0}", count)
+            };
+        }
+
+        private static Dictionary<Numberz, long> CreateUserMap(int count)
+        {
+            var retval = new Dictionary<Numberz, long>();
+            retval.Add(Numberz.ONE, count);
+            retval.Add(Numberz.TWO, count);
+            retval.Add(Numberz.THREE, count);
+            retval.Add(Numberz.FIVE, count);
+            retval.Add(Numberz.SIX, count);
+            retval.Add(Numberz.EIGHT, count);
+            return retval;
+        }
+
+        private static List<Dictionary<THashSet<int>, Dictionary<int, THashSet<List<Dictionary<Insanity, string>>>>>> CreateListField(int count)
+        {
+            var retval = new List<Dictionary<THashSet<int>, Dictionary<int, THashSet<List<Dictionary<Insanity, string>>>>>>();
+            for (var i = 0; i < count; ++i)
+                retval.Add(CreateListFieldData(count));
+            return retval;
+        }
+
+        private static Dictionary<THashSet<int>, Dictionary<int, THashSet<List<Dictionary<Insanity, string>>>>> CreateListFieldData(int count)
+        {
+            var retval = new Dictionary<THashSet<int>, Dictionary<int, THashSet<List<Dictionary<Insanity, string>>>>>();
+            for (var i = 0; i < count; ++i)
+                retval.Add( CreateIntHashSet(count), CreateListFieldDataDict(count));
+            return retval;
+        }
+
+        private static THashSet<int> CreateIntHashSet(int count)
+        {
+            var retval = new THashSet<int>();
+            for (var i = 0; i < count; ++i)
+                retval.Add(i);
+            return retval;
+        }
+
+        private static Dictionary<int, THashSet<List<Dictionary<Insanity, string>>>> CreateListFieldDataDict(int count)
+        {
+            var retval = new Dictionary<int, THashSet<List<Dictionary<Insanity, string>>>>();
+            for (var i = 0; i < count; ++i)
+                retval.Add(i, CreateListFieldDataDictValue(count));
+            return retval;
+        }
+
+        private static THashSet<List<Dictionary<Insanity, string>>> CreateListFieldDataDictValue(int count)
+        {
+            var retval = new THashSet<List<Dictionary<Insanity, string>>>();
+            for (var i = 0; i < count; ++i)
+                retval.Add( CreateListFieldDataDictValueList(count));
+            return retval;
+        }
+
+        private static List<Dictionary<Insanity, string>> CreateListFieldDataDictValueList(int count)
+        {
+            var retval = new List<Dictionary<Insanity, string>>();
+            for (var i = 0; i < count; ++i)
+                retval.Add(CreateListFieldDataDictValueListDict(count));
+            return retval;
+        }
+
+        private static Dictionary<Insanity, string> CreateListFieldDataDictValueListDict(int count)
+        {
+            var retval = new Dictionary<Insanity, string>();
+            retval.Add(CreateInsanity(count), string.Format("data level {0}", count));
+            return retval;
+        }
+
+        private static byte[] CreateBytesArray(int count)
+        {
+            var retval = new byte[count];
+            for (var i = 0; i < count; ++i)
+                retval[i] = (byte)(i % 0xFF);
+            return retval;
+        }
+    }
+}
diff --git a/test/netstd/Client/Program.cs b/test/netstd/Client/Program.cs
index 8d973c4..62933e6 100644
--- a/test/netstd/Client/Program.cs
+++ b/test/netstd/Client/Program.cs
@@ -48,6 +48,8 @@
             {
                 case "client":
                     return TestClient.Execute(subArgs);
+                case "performance":
+                    return Tests.PerformanceTests.Execute();
                 case "--help":
                     PrintHelp();
                     return 0;
@@ -61,7 +63,8 @@
         private static void PrintHelp()
         {
             Console.WriteLine("Usage:");
-            Console.WriteLine("  Client  client  [options]'");
+            Console.WriteLine("  Client  client  [options]");
+            Console.WriteLine("  Client  performance");
             Console.WriteLine("  Client  --help");
             Console.WriteLine("");