Информация об изменениях

Сообщение Re[29]: Реальная производительность WebAssembly? от 19.09.2017 20:53

Изменено 19.09.2017 21:12 alexzzzz

Re[29]: Реальная производительность WebAssembly?
Здравствуйте, alexzzzz, Вы писали:

A>GC: 113 46 13

Поглядывал периодически на подобные строчки и казались они мне какими-то подозрительными. Что-то слишком много сборок мусора. Раньше такого не наблюдал. Или, может, в Unity отвык.

Короче, добавил в конфиг такую строчку и сразу полегчало:
<gcServer enabled="true"/>


В результате имеем:
  C#, один поток
using System;
using System.Diagnostics;

namespace QuickSort
{
    public class Program
    {
        public static void Main()
        {
            const int ARRAY_SIZE = 1000 * 1000;

            var watch = new Stopwatch();
            for (var i = 0; i < 5; i++)
            {
                GC.Collect();
                watch.Restart();

                var vals = new TestStruct[ARRAY_SIZE];
                for (int j = 0; j < vals.Length; j++)
                {
                    vals[j] = TestStruct.Create();
                }

                watch.Stop();
                Console.WriteLine($"Total: {watch.ElapsedMilliseconds} ms");
            }
            Console.WriteLine($"GC: {GC.CollectionCount(0)} {GC.CollectionCount(1)} {GC.CollectionCount(2)}");
            Console.ReadLine();
        }
    }

    internal unsafe struct TestStruct
    {
        public string id;
        public string value;
        private static readonly Random random = new Random();

        public static TestStruct Create()
        {
            TestStruct item;
            item.id = GenerateRandomNumber();
            item.value = GenerateRandomNumber();
            return item;
        }

        public override string ToString() => id;

        private static string GenerateRandomNumber()
        {
            var buffer = stackalloc char[11];
            int num = random.Next(100_000_000);
            var p = buffer;
            *p++ = '0';
            *p++ = '0';
            *p++ = (char)('0' + num % 10); num /= 10;
            *p++ = (char)('0' + num % 10); num /= 10;
            *p++ = (char)('0' + num % 10); num /= 10;
            *p++ = (char)('0' + num % 10); num /= 10;
            *p++ = (char)('0' + num % 10); num /= 10;
            *p++ = (char)('0' + num % 10); num /= 10;
            *p++ = (char)('0' + num % 10); num /= 10;
            *p = (char)('0' + num % 10);
            return new string(buffer);
        }
    }
}

Total: 155 ms
Total: 116 ms
Total: 112 ms
Total: 113 ms
Total: 112 ms
GC: 7 6 6

  C#, четыре потока
using System;
using System.Diagnostics;
using System.Threading;

namespace QuickSort
{
    public class Program
    {
        private static int activeThreadCount;
        private static ManualResetEvent waitHandle = new ManualResetEvent(false);

        public static void Main()
        {
            const int ARRAY_SIZE = 1000 * 1000;
            const int THREADS = 4;

            var watch = new Stopwatch();
            for (var i = 0; i < 5; i++)
            {
                GC.Collect();
                watch.Restart();
                waitHandle.Reset();

                var vals = new TestClass[ARRAY_SIZE];
                activeThreadCount = THREADS;
                for (var j = 0; j < THREADS; j++)
                {
                    int segmentIndex = j;
                    ThreadPool.QueueUserWorkItem(_ => Fill(vals, segmentIndex * (ARRAY_SIZE / THREADS), ARRAY_SIZE / THREADS));
                }
                waitHandle.WaitOne();

                watch.Stop();
                Console.WriteLine($"Total: {watch.ElapsedMilliseconds} ms");
            }
            Console.WriteLine($"GC: {GC.CollectionCount(0)} {GC.CollectionCount(1)} {GC.CollectionCount(2)}");
            Console.ReadLine();
        }

        private static void Fill(TestClass[] array, int start, int count)
        {
            int end = start + count;
            for (int i = start; i < end; i++)
            {
                array[i] = new TestClass();
            }

            Interlocked.Decrement(ref activeThreadCount);
            if (activeThreadCount == 0)
            {
                waitHandle.Set();
            }
        }
    }

    internal class TestClass
    {
        public string id = GenerateRandomNumber();
        public string value = GenerateRandomNumber();
        private static ThreadLocal<Random> localRandom = new ThreadLocal<Random>(() => new Random());

        public override string ToString() => id;

        private static unsafe string GenerateRandomNumber()
        {
            var num = localRandom.Value.Next(100_000_000);
            var chars = stackalloc char[11]; // null-terminated 10-char string
            chars[0] = '0';
            chars[1] = '0';
            chars[2] = (char)('0' + num % 10); num /= 10;
            chars[3] = (char)('0' + num % 10); num /= 10;
            chars[4] = (char)('0' + num % 10); num /= 10;
            chars[5] = (char)('0' + num % 10); num /= 10;
            chars[6] = (char)('0' + num % 10); num /= 10;
            chars[7] = (char)('0' + num % 10); num /= 10;
            chars[8] = (char)('0' + num % 10); num /= 10;
            chars[9] = (char)('0' + num % 10);
            return new string(chars);
        }
    }
}

Total: 91 ms
Total: 45 ms
Total: 42 ms
Total: 43 ms
Total: 45 ms
GC: 7 6 6
Re[29]: Реальная производительность WebAssembly?
Здравствуйте, alexzzzz, Вы писали:

A>GC: 113 46 13

Поглядывал периодически на подобные строчки и казались они мне какими-то подозрительными. Что-то слишком много сборок мусора. Раньше такого не наблюдал. Или, может, в Unity отвык.

Короче, добавил в конфиг такую строчку и сразу полегчало:
<gcServer enabled="true"/>

В результате имеем:
  C#, один поток
using System;
using System.Diagnostics;

namespace QuickSort
{
    public class Program
    {
        public static void Main()
        {
            const int ARRAY_SIZE = 1000 * 1000;

            var watch = new Stopwatch();
            for (var i = 0; i < 5; i++)
            {
                GC.Collect();
                watch.Restart();

                var vals = new TestStruct[ARRAY_SIZE];
                for (int j = 0; j < vals.Length; j++)
                {
                    vals[j] = TestStruct.Create();
                }

                watch.Stop();
                Console.WriteLine($"Total: {watch.ElapsedMilliseconds} ms");
            }
            Console.WriteLine($"GC: {GC.CollectionCount(0)} {GC.CollectionCount(1)} {GC.CollectionCount(2)}");
            Console.ReadLine();
        }
    }

    internal unsafe struct TestStruct
    {
        public string id;
        public string value;
        private static readonly Random random = new Random();

        public static TestStruct Create()
        {
            TestStruct item;
            item.id = GenerateRandomNumber();
            item.value = GenerateRandomNumber();
            return item;
        }

        public override string ToString() => id;

        private static string GenerateRandomNumber()
        {
            var buffer = stackalloc char[11];
            int num = random.Next(100_000_000);
            var p = buffer;
            *p++ = '0';
            *p++ = '0';
            *p++ = (char)('0' + num % 10); num /= 10;
            *p++ = (char)('0' + num % 10); num /= 10;
            *p++ = (char)('0' + num % 10); num /= 10;
            *p++ = (char)('0' + num % 10); num /= 10;
            *p++ = (char)('0' + num % 10); num /= 10;
            *p++ = (char)('0' + num % 10); num /= 10;
            *p++ = (char)('0' + num % 10); num /= 10;
            *p = (char)('0' + num % 10);
            return new string(buffer);
        }
    }
}

Total: 155 ms
Total: 116 ms
Total: 112 ms
Total: 113 ms
Total: 112 ms
GC: 7 6 6

  C#, четыре потока
using System;
using System.Diagnostics;
using System.Threading;

namespace QuickSort
{
    public class Program
    {
        private static int activeThreadCount;
        private static ManualResetEvent waitHandle = new ManualResetEvent(false);

        public static void Main()
        {
            const int ARRAY_SIZE = 1000 * 1000;
            const int THREADS = 4;

            var watch = new Stopwatch();
            for (var i = 0; i < 5; i++)
            {
                GC.Collect();
                watch.Restart();
                waitHandle.Reset();

                var vals = new TestClass[ARRAY_SIZE];
                activeThreadCount = THREADS;
                for (var j = 0; j < THREADS; j++)
                {
                    int segmentIndex = j;
                    ThreadPool.QueueUserWorkItem(_ => Fill(vals, segmentIndex * (ARRAY_SIZE / THREADS), ARRAY_SIZE / THREADS));
                }
                waitHandle.WaitOne();

                watch.Stop();
                Console.WriteLine($"Total: {watch.ElapsedMilliseconds} ms");
            }
            Console.WriteLine($"GC: {GC.CollectionCount(0)} {GC.CollectionCount(1)} {GC.CollectionCount(2)}");
            Console.ReadLine();
        }

        private static void Fill(TestClass[] array, int start, int count)
        {
            int end = start + count;
            for (int i = start; i < end; i++)
            {
                array[i] = new TestClass();
            }

            Interlocked.Decrement(ref activeThreadCount);
            if (activeThreadCount == 0)
            {
                waitHandle.Set();
            }
        }
    }

    internal class TestClass
    {
        public string id = GenerateRandomNumber();
        public string value = GenerateRandomNumber();
        private static ThreadLocal<Random> localRandom = new ThreadLocal<Random>(() => new Random());

        public override string ToString() => id;

        private static unsafe string GenerateRandomNumber()
        {
            var num = localRandom.Value.Next(100_000_000);
            var chars = stackalloc char[11]; // null-terminated 10-char string
            chars[0] = '0';
            chars[1] = '0';
            chars[2] = (char)('0' + num % 10); num /= 10;
            chars[3] = (char)('0' + num % 10); num /= 10;
            chars[4] = (char)('0' + num % 10); num /= 10;
            chars[5] = (char)('0' + num % 10); num /= 10;
            chars[6] = (char)('0' + num % 10); num /= 10;
            chars[7] = (char)('0' + num % 10); num /= 10;
            chars[8] = (char)('0' + num % 10); num /= 10;
            chars[9] = (char)('0' + num % 10);
            return new string(chars);
        }
    }
}

Total: 91 ms
Total: 45 ms
Total: 42 ms
Total: 43 ms
Total: 45 ms
GC: 7 6 6


Добавил эти тесты в репозиторий, добавил app.config во все проекты, пересобрал все бинарники.
https://bitbucket.org/alexzzzz/perftest