Re[9]: lock ws ReaderWriterLockSlim: интерес?
От: mihhon  
Дата: 23.01.11 15:42
Оценка:
D>Цифирь итоговая от запуска к запуску может влёгкую на порядок различаться.
не различалась

D>В том и дело, что нет там никакой очереди. lock успевает отработать за квант, а ядер всего два, и ThreadPool не стартует worker'ов больше чем ядер. В итоге блокировок практически не бывает.

ThreadPool стартует новые threads, если количество задач в очереди не меняется в течение 0,5секунды, тесты по 5секунд, т.е. к концу теста 11-12 threads

D>Как там автор нулевую загрузку увидел — не понимаю. Она — после "разогрева" ThreadPool'а — тоже должна быть около 100%. Разве что у автора нетбук

не знаю, посмотрю на большем отрезке времени
Re[10]: lock ws ReaderWriterLockSlim: интерес?
От: drol  
Дата: 23.01.11 16:48
Оценка:
Здравствуйте, mihhon, Вы писали:

D>>Цифирь итоговая от запуска к запуску может влёгкую на порядок различаться.

M>не различалась

Значит Вы что-то делаете не так Например, пускаете вовсе не тот код, что опубликовали на форуме; в Debug сборке, а не в Release; и т.п.

Я вот прямо сейчас запустил, ради прикола:
TestReaderWriterMultiTime: timer 5000ms, readersCount 50, writers 1, reads 18486018, writes 5084, readLock 270.474690655392ns, writeLock 983477.576711251ns
TestReaderWriterMultiTime: timer 5000ms, readersCount 50, writers 1, reads 826139, writes 1071, readLock 6052.25028717928ns, writeLock 4668534.08029879ns

M>ThreadPool стартует новые threads, если количество задач в очереди не меняется в течение 0,5секунды, тесты по 5секунд, т.е. к концу теста 11-12 threads

Да нипричём тут количество потоков. Просто в Вашем "тесте" lock'а блокировки практически никогда не возникают. Происходит это бо шаг Вашего цикла нагрузки почти в точности равен кванту — загадочный метод MakeTemporization() обеспечивает это явным образом. Посему когда следующий поток отправляется на исполнение — lock уже свободен.

D>>Как там автор нулевую загрузку увидел — не понимаю. Она — после "разогрева" ThreadPool'а — тоже должна быть около 100%. Разве что у автора нетбук

M>не знаю, посмотрю на большем отрезке времени

Вы лучше железо озвучьте, для начала.
Re[11]: lock ws ReaderWriterLockSlim: интерес?
От: mihhon  
Дата: 24.01.11 08:45
Оценка:
D>Вы лучше железо озвучьте, для начала.
Intel® Core™2 Duo Processor E8400
# of Cores 2
# of Threads 2

есть ещё такой, на нём 4 ядра
Intel® Xeon® Processor L5520
# of Cores 4
# of Threads 8
Re[11]: lock ws ReaderWriterLockSlim: интерес?
От: mihhon  
Дата: 24.01.11 08:51
Оценка:
D>Вы лучше железо озвучьте, для начала.
тот что xeon — hp proliant dl360 g6 , 2 процессора, task manager и device manager показывают 16 процессоров
Re[8]: lock ws ReaderWriterLockSlim: интерес?
От: mihhon  
Дата: 24.01.11 09:49
Оценка:
vf>Смотри показатели для ReaderWriterLock для 11 потоков значительно улучшаются. 51 поток это неэфективно для двухядерного процессора, с гипертриденгом, ИМХО, как раз 4-6 ну может 8 потоков и должно работать для максимальной отдачи.

камень в огород FastResourceLock: на 2 х Хeon L5520 — hp proliant dl360 g6, на разогретом ThreadPool
            int threadNum = writersCount + readersCount;
            ThreadPool.SetMinThreads(threadNum, threadNum);
            ThreadPool.SetMaxThreads(threadNum, threadNum);

, на 50 читающих threads какая-то бага , блокируются все читатели хороший такой троянский конь в production может получиться

D:\homeware\tmp>test 5000 15 1
TestLockMultiTime: timer 5000ms, readersCount 1, writersCount 15, reads 1607035, writes 603576, readLock 3111.31991524765ns, writeLock 8283.9609262131ns
TestLockMultiTime: timer 5000ms, readersCount 1, writersCount 15, reads 1594988, writes 608307, readLock 3134.81982309585ns, writeLock 8219.53388667235ns
TestLockMultiTime: timer 5000ms, readersCount 1, writersCount 15, reads 1590231, writes 602751, readLock 3144.19728957617ns, writeLock 8295.29938565013ns
TestReaderWriterMultiTime: timer 5000ms, readersCount 1, writersCount 15, reads 448730, writes 371055, readLock 11142.5578855882ns, writeLock 13475.0912937435ns
TestReaderWriterMultiTime: timer 5000ms, readersCount 1, writersCount 15, reads 269034, writes 375105, readLock 18585.0115598772ns, writeLock 13329.6010450407ns
TestFastResourceLockMultiTime: timer 5000ms, readersCount 1, writersCount 15, reads 1104259, writes 1005134, readLock 4527.92324988974ns, writeLock 4974.46111662724ns
TestFastResourceLockMultiTime: timer 5000ms, readersCount 1, writersCount 15, reads 1146701, writes 994956, readLock 4360.3345597501ns, writeLock 5025.34785457849ns

D:\homeware\tmp>test 5000 7 1
TestLockMultiTime: timer 5000ms, readersCount 1, writersCount 7, reads 2073003, writes 287957, readLock 2411.95984762202ns, writeLock 17363.7036085249ns
TestLockMultiTime: timer 5000ms, readersCount 1, writersCount 7, reads 2147753, writes 295181, readLock 2328.01444113918ns, writeLock 16938.7596085114ns
TestLockMultiTime: timer 5000ms, readersCount 1, writersCount 7, reads 2135838, writes 295643, readLock 2341.00151790538ns, writeLock 16912.2894842766ns
TestReaderWriterMultiTime: timer 5000ms, readersCount 1, writersCount 7, reads 1050802, writes 243831, readLock 4758.2703496948ns, writeLock 20506.0062092187ns
TestReaderWriterMultiTime: timer 5000ms, readersCount 1, writersCount 7, reads 959999, writes 244101, readLock 5208.33875868621ns, writeLock 20483.3245255038ns
TestFastResourceLockMultiTime: timer 5000ms, readersCount 1, writersCount 7, reads 1597978, writes 752734, readLock 3128.95421589033ns, writeLock 6642.45271237914ns
TestFastResourceLockMultiTime: timer 5000ms, readersCount 1, writersCount 7, reads 1798527, writes 780656, readLock 2780.05278764233ns, writeLock 6404.86975056875ns

D:\homeware\tmp>test 5000 50 1
TestLockMultiTime: timer 5000ms, readersCount 1, writersCount 50, reads 275440, writes 1628526, readLock 18152.7737438281ns, writeLock 3070.26108272143ns
TestLockMultiTime: timer 5000ms, readersCount 1, writersCount 50, reads 238900, writes 1435977, readLock 20929.2591042277ns, writeLock 3481.94991981069ns
TestLockMultiTime: timer 5000ms, readersCount 1, writersCount 50, reads 239186, writes 1536342, readLock 20904.2335253736ns, writeLock 3254.48370219652ns
TestReaderWriterMultiTime: timer 5000ms, readersCount 1, writersCount 50, reads 88512, writes 488839, readLock 56489.5155459147ns, writeLock 10228.3164804772ns
TestReaderWriterMultiTime: timer 5000ms, readersCount 1, writersCount 50, reads 136403, writes 426882, readLock 36656.0852767168ns, writeLock 11712.8386767303ns
TestFastResourceLockMultiTime: timer 5000ms, readersCount 1, writersCount 50, reads 1, writes 834111, readLock 5000000000ns, writeLock 5994.40602030185ns
TestFastResourceLockMultiTime: timer 5000ms, readersCount 1, writersCount 50, reads 1, writes 830261, readLock 5000000000ns, writeLock 6022.20265675492ns
Re[9]: lock ws ReaderWriterLockSlim: интерес?
От: mihhon  
Дата: 24.01.11 09:55
Оценка:
перепутал ридеров и райтеров, но бага есть всё равно
Re[11]: lock ws ReaderWriterLockSlim: интерес?
От: mihhon  
Дата: 24.01.11 11:01
Оценка:
D>Вы лучше железо озвучьте, для начала.

я поправил тест, ThreadPool сразу содержит требуемое количство threads, и размер пула не меняется

MakeTemporization — это метод, который должен позволить моделизировать "реальную" нагрузку приложения: чтоб количество записей было в XXX раз меньше чтений , я не знаю как лучше его реализовать : Thread.Sleep(1) с учётом точности таймера не самое лучшее решение. я поправил его : убрал Thread.Sleep(0).

в итоге: если смотреть на цифры, я не вижу никакой выгоды от ReaderWriterLockSlim: он всё время поразывает худший результат, чем lock, а FastResourceLock , похоже с багами

начальный вопрос: когда же выгоднее использовать ReaderWriterLockSlim, сколько процессоров и какое соотношение чтений/записей ?



2x Xeon 4cores = 8 cores

10000 reads for 1 write:

D:\homeware\tmp>test 5000 1 7
TestLockMultiTime: timer 5000ms, readersCount 7, writersCount 1, reads 9108150, writes 221401
TestLockMultiTime: timer 5000ms, readersCount 7, writersCount 1, reads 8521472, writes 210999
TestLockMultiTime: timer 5000ms, readersCount 7, writersCount 1, reads 9302736, writes 215200
TestReaderWriterMultiTime: timer 5000ms, readersCount 7, writersCount 1, reads 8278425, writes 28215
TestReaderWriterMultiTime: timer 5000ms, readersCount 7, writersCount 1, reads 7152855, writes 36786
TestFastResourceLockMultiTime: timer 5000ms, readersCount 7, writersCount 1, reads 16821177, writes 59111
TestFastResourceLockMultiTime: timer 5000ms, readersCount 7, writersCount 1, reads 16514123, writes 61588

D:\homeware\tmp>test 5000 1 15
TestLockMultiTime: timer 5000ms, readersCount 15, writersCount 1, reads 8846441, writes 167201
TestLockMultiTime: timer 5000ms, readersCount 15, writersCount 1, reads 9069098, writes 175200
TestLockMultiTime: timer 5000ms, readersCount 15, writersCount 1, reads 9120507, writes 170600
TestReaderWriterMultiTime: timer 5000ms, readersCount 15, writersCount 1, reads 4633077, writes 12402
TestReaderWriterMultiTime: timer 5000ms, readersCount 15, writersCount 1, reads 4524697, writes 15409
TestFastResourceLockMultiTime: timer 5000ms, readersCount 15, writersCount 1, reads 14846097, writes 17651
TestFastResourceLockMultiTime: timer 5000ms, readersCount 15, writersCount 1, reads 14639979, writes 17052

D:\homeware\tmp>test 5000 1 50
TestLockMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 9050320, writes 30625
TestLockMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 9046398, writes 28523
TestLockMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 8908890, writes 30653
TestReaderWriterMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 3681543, writes 821
TestReaderWriterMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 3520756, writes 356
TestFastResourceLockMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 12145279, writes 4023
TestFastResourceLockMultiTime: timer 5000ms, readersCount 50, writersCount 1, reads 15444010, writes 2111



    public class LockTestProgram
    {
        [DllImport("kernel32.dll")]
        static extern bool SwitchToThread();

        static IDictionary<string, string> dictionary = new Dictionary<string, string>();

        public static void Main(string[] args)
        {
            Thread.CurrentThread.Name = "Main";

            if (args.Length != 3)
            {
                System.Console.WriteLine("usage: progName time(millis) writersCount readersCount");
                return;
            }

            for (int i = 0; i < 1000000; i++)
            {
                dictionary[i.ToString()] = i.ToString();
            }

            int millis = int.Parse(args[0]);
            int writersCount = int.Parse(args[1]);
            int readersCount = int.Parse(args[2]);

            int threadNum = writersCount + readersCount;
            ThreadPool.SetMinThreads(threadNum, threadNum);
            ThreadPool.SetMaxThreads(threadNum, threadNum);

            // warm the pool
            TestLockMultiTime(millis, writersCount, readersCount);
            Thread.Sleep(200);

            TestLockMultiTime(millis, writersCount, readersCount);
            Thread.Sleep(200);
            TestLockMultiTime(millis, writersCount, readersCount);
            Thread.Sleep(200);
            TestReaderWriterMultiTime(millis, writersCount, readersCount);
            Thread.Sleep(200);
            TestReaderWriterMultiTime(millis, writersCount, readersCount);
            Thread.Sleep(200);
            TestFastResourceLockMultiTime(millis, writersCount, readersCount);
            Thread.Sleep(200);
            TestFastResourceLockMultiTime(millis, writersCount, readersCount);

            // multithreaded with fixed number of interations
            //            if (args.Length != 3)
            //            {
            //                System.Console.WriteLine("usage: progName load writersCount readersCount");
            //                return;
            //            }
            //            
            //            int load = int.Parse(args[0]);
            //            int writersCount = int.Parse(args[1]);
            //            int readersCount = int.Parse(args[2]);
            //
            //            TestLockMulti(load, writersCount, readersCount);
            //            TestLockMulti(load, writersCount, readersCount);
            //            TestReaderWriterMulti(load, writersCount, readersCount);
            //            TestReaderWriterMulti(load, writersCount, readersCount);

            // monothreaded
            //            TestEmptyLoop(load);
            //            TestEmptyLoop(load);
            //            TestLock(load);
            //            TestLock(load);
            //            TestReaderWriterLockSlim_Read(load);
            //            TestReaderWriterLockSlim_Read(load);
            //            TestReaderWriterLockSlim_Write(load);
            //            TestReaderWriterLockSlim_Write(load);
        }

        #region empty loop estimation

        private static void TestEmptyLoop(int load)
        {
            int a = 0;
            Stopwatch start = Stopwatch.StartNew();
            for (int i = 0; i < load; i++)
            {
                a++;
            }
            long elapsed = start.ElapsedMilliseconds;
            System.Console.WriteLine(
                string.Format(
                "TestEmptyLoop: load {0}, elapsed millis {1}, wo locking {2}ns",
                load, elapsed, ((double)elapsed) / load * 1000000));
        }

        #endregion

        #region multithreaded during the fixed time

        private static void TestLockMultiTime(int millis, int writersCount, int readersCount)
        {
            long reads = 0;
            long writes = 0;

            object obj = new object();
            bool flag = true;
            string b;
            ThreadStart writer = delegate()
            {
                while (flag)
                {
                    lock (obj)
                    {
                        long localW = Interlocked.Increment(ref writes);
                        localW %= dictionary.Count;
                        dictionary[localW.ToString()] = localW.ToString();
                    }
                    MakeTemporization(false);
                }
            };

            ThreadStart reader = delegate()
            {
                while (flag)
                {
                    lock (obj)
                    {
                        long localW = Interlocked.Increment(ref reads);
                        localW %= dictionary.Count;
                        b = dictionary[localW.ToString()];
                    }
                    MakeTemporization(true);
                }
            };

            Stopwatch start = Stopwatch.StartNew();

            // start threads
            IAsyncResult[] writerResults = StartThreads(writersCount, writer);
            IAsyncResult[] readerResults = StartThreads(readersCount, reader);

            Thread.Sleep(millis);
            flag = false;

            // wait for the execution
            WaitThreads(writer, writerResults);
            WaitThreads(reader, readerResults);

            long elapsed = start.ElapsedMilliseconds;
            System.Console.WriteLine(
                string.Format(
                "TestLockMultiTime: timer {0}ms, readersCount {1}, writersCount {2}, reads {3}, writes {4}",
                millis, readersCount, writersCount, reads, writes));
        }


        private static void TestReaderWriterMultiTime(int millis, int writersCount, int readersCount)
        {
            long reads = 0;
            long writes = 0;

            ReaderWriterLockSlim LOCK = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
            bool flag = true;
            string b;
            ThreadStart writer = delegate()
            {
                while (flag)
                {
                    LOCK.EnterWriteLock();
                    try
                    {
                        long localW = Interlocked.Increment(ref writes);
                        localW %= dictionary.Count;
                        dictionary[localW.ToString()] = localW.ToString();
                    }
                    finally
                    {
                        LOCK.ExitWriteLock();
                    }
                    MakeTemporization(false);
                }
            };

            ThreadStart reader = delegate()
            {
                while (flag)
                {
                    LOCK.EnterReadLock();
                    try
                    {
                        long localW = Interlocked.Increment(ref reads);
                        localW %= dictionary.Count;
                        b = dictionary[localW.ToString()];
                    }
                    finally
                    {
                        LOCK.ExitReadLock();
                    }
                    MakeTemporization(true);
                }
            };

            Stopwatch start = Stopwatch.StartNew();

            // start threads
            IAsyncResult[] writerResults = StartThreads(writersCount, writer);
            IAsyncResult[] readerResults = StartThreads(readersCount, reader);

            Thread.Sleep(millis);
            flag = false;

            // wait for the execution
            WaitThreads(writer, writerResults);
            WaitThreads(reader, readerResults);

            long elapsed = start.ElapsedMilliseconds;
            System.Console.WriteLine(
                string.Format(
                "TestReaderWriterMultiTime: timer {0}ms, readersCount {1}, writersCount {2}, reads {3}, writes {4}",
                millis, readersCount, writersCount, reads, writes));
        }


        private static void TestFastResourceLockMultiTime(int millis, int writersCount, int readersCount)
        {
            long reads = 0;
            long writes = 0;

            FastResourceLock LOCK = new FastResourceLock();
            bool flag = true;
            string b;
            ThreadStart writer = delegate()
            {
                while (flag)
                {
                    LOCK.AcquireExclusive();
                    try
                    {
                        long localW = Interlocked.Increment(ref writes);
                        localW %= dictionary.Count;
                        dictionary[localW.ToString()] = localW.ToString();
                    }
                    finally
                    {
                        LOCK.ReleaseExclusive();
                    }
                    MakeTemporization(false);
                }
            };

            ThreadStart reader = delegate()
            {
                while (flag)
                {
                    LOCK.AcquireShared();
                    try
                    {
                        long localW = Interlocked.Increment(ref reads);
                        localW %= dictionary.Count;
                        b = dictionary[localW.ToString()];
                    }
                    finally
                    {
                        LOCK.ReleaseShared();
                    }
                    MakeTemporization(true);
                }
            };

            Stopwatch start = Stopwatch.StartNew();

            // start threads
            IAsyncResult[] writerResults = StartThreads(writersCount, writer);
            IAsyncResult[] readerResults = StartThreads(readersCount, reader);

            Thread.Sleep(millis);
            flag = false;

            // wait for the execution
            WaitThreads(writer, writerResults);
            WaitThreads(reader, readerResults);

            long elapsed = start.ElapsedMilliseconds;
            System.Console.WriteLine(
                string.Format(
                "TestFastResourceLockMultiTime: timer {0}ms, readersCount {1}, writersCount {2}, reads {3}, writes {4}",
                millis, readersCount, writersCount, reads, writes));
        }

        private static int sleepReadCounter;
        private static int sleepWriteCounter;
        private static void MakeTemporization(bool read)
        {
            // 1- implementation 1: write to console
            //            System.Console.WriteLine("  ");


            // 2- implementation 2: sleep
            int step = read ? 2000000 : 200;

            int localSleepCounter = read 
                ? Interlocked.Increment(ref sleepReadCounter)
                : Interlocked.Increment(ref sleepWriteCounter);
            if (localSleepCounter % step == 0)
            {
                Thread.Sleep(1);
            }
            else
            {
                //Thread.Sleep(0);
                //SwitchToThread();
            }

            // 3- implementation: switch the thread
            //            SwitchToThread();
        }

        #endregion

        #region multithreaded with fixed number of interations

        private static void TestLockMulti(int load, int writersCount, int readersCount)
        {
            int a = 0;
            int b = 0;
            object obj = new object();

            ThreadStart writer = delegate()
            {
                for (int i = 0; i < load; i++)
                {
                    lock (obj)
                    {
                        a++;
                    }
                }
            };

            ThreadStart reader = delegate()
            {
                for (int i = 0; i < load; i++)
                {
                    lock (obj)
                    {
                        b = a;
                    }
                }
            };

            Stopwatch start = Stopwatch.StartNew();

            // start threads
            IAsyncResult[] writerResults = StartThreads(writersCount, writer);
            IAsyncResult[] readerResults = StartThreads(readersCount, reader);

            // wait for the execution
            WaitThreads(writer, writerResults);
            WaitThreads(reader, readerResults);

            long elapsed = start.ElapsedMilliseconds;
            System.Console.WriteLine(
                string.Format(
                "TestLockMulti: load {0}, readersCount {1}, writersCount {2}, elapsed millis {3}, 1 lock takes {4}ns",
                load, readersCount, writersCount, elapsed, ((double)elapsed) / load * 1000000));
        }

        private static void TestReaderWriterMulti(int load, int writersCount, int readersCount)
        {
            int a = 0;
            int b = 0;
            ReaderWriterLockSlim LOCK = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

            ThreadStart writer = delegate()
            {
                for (int i = 0; i < load; i++)
                {
                    LOCK.EnterWriteLock();
                    try
                    {
                        a++;
                    }
                    finally
                    {
                        LOCK.ExitWriteLock();
                    }
                }
            };

            ThreadStart reader = delegate()
            {
                for (int i = 0; i < load; i++)
                {
                    LOCK.EnterReadLock();
                    try
                    {
                        b = a;
                    }
                    finally
                    {
                        LOCK.ExitReadLock();
                    }
                }
            };

            Stopwatch start = Stopwatch.StartNew();

            // start threads
            IAsyncResult[] writerResults = StartThreads(writersCount, writer);
            IAsyncResult[] readerResults = StartThreads(readersCount, reader);

            // wait for the execution
            WaitThreads(writer, writerResults);
            WaitThreads(reader, readerResults);

            long elapsed = start.ElapsedMilliseconds;
            System.Console.WriteLine(
                string.Format(
                "TestReaderWriterMulti: load {0}, readersCount {1}, writersCount {2}, elapsed millis {3}, 1 lock takes {4}ns",
                load, readersCount, writersCount, elapsed, ((double)elapsed) / load * 1000000));
        }

        private static IAsyncResult[] StartThreads(int count, ThreadStart threadStart)
        {
            IAsyncResult[] results = new IAsyncResult[count];
            for (int i = 0; i < count; i++)
            {
                results[i] = threadStart.BeginInvoke(delegate(IAsyncResult ar) { }, null);
            }
            return results;
        }

        private static void WaitThreads(ThreadStart threadStart, IAsyncResult[] results)
        {
            for (int i = 0; i < results.Length; i++)
            {
                threadStart.EndInvoke(results[i]);
            }
        }
    }
Re[12]: lock ws ReaderWriterLockSlim: интерес?
От: vf  
Дата: 24.01.11 12:59
Оценка:
Здравствуйте, mihhon, Вы писали:

M>FastResourceLock , похоже с багами


When that thread is done with the lock, it clears the L bit (so other threads can set it to acquire the lock). If the thread sees that there is an exclusive waiter (by looking at EW), it releases the semaphore once, waking up an exclusive waiter. If there are no exclusive waiters but there are shared waiters, it releases all of them at once (since they can all acquire the lock at the same time – that is the whole point of a reader-writer lock).


Там нет очереди... Это не баг, это особенность. Думаю при желании такое поведение можно легко изменить, хотя 50 писателей и 1 читатель не совсем та ситуации, а этот механизм без претензий на универсальность?!
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.