Недавно получил простую задачу: написать windows-сервис для обработки пользовательских запросов. Вопрос про то, какие эти запросы и по какому протоколу работает сервис, выходит за рамки этой статьи. Более интересным мне показался другой фактор, многопоточную ли делать обработку запросов. С одной стороны — последовательное выполнение тормозит процес обработки информации. С другой стороны могут быть не оправданы затраты на создание и запуск потока.
Итак, исходные данные: 20 простейших запросов в секунду (1200 запросов в минуту) в пиковое время. Тестовый «сервер»: Celeron, 3ГЦ, 1Гб (свободно 70%).
Сначала напишем класс-базу для однопоточного выполнения запросов.
Запустим программу с несколькими параметрами задержки выполнения запроса: 2, 5, 10
Как видим память практически не страдает, а время примерно равно (mockSpeed+1)*1200. Дополнительную миллисекунду спишем на накладные расходы.
Перепишем программу для работы с многопоточностью, оптимизируем ее и сверим результаты:
При тестировании производительности многопоточности появилась новая величина время запуска процессов. Именно на эту величину вырастает общая продолжительность выполнения программы. Примерный запуск процесса равен 0,5миллисекундам. Также мы видим, значительно выросший объем используемой памяти, которая расходуется на стек запускаемых потоков.
Выделим все сравниваемые величины в таблицу.
Вот такая «студенческая лабораторная работа» вышла при изучении такого вопроса. Прошу не кидать камни :)
Итак, исходные данные: 20 простейших запросов в секунду (1200 запросов в минуту) в пиковое время. Тестовый «сервер»: Celeron, 3ГЦ, 1Гб (свободно 70%).
Однопоточная система
Сначала напишем класс-базу для однопоточного выполнения запросов.
- using System;
- using System.Diagnostics;
- using System.Threading;
-
- namespace TestConsoleApplication
- {
-
- class mockClass
- {
- private readonly Int32 incriment_speed;
- private Int32 inc;
-
- public mockClass(int incriment_speed)
- {
- this.incriment_speed = incriment_speed;
- inc = 0;
- }
-
- public Int32 incriment()
- {
- Thread.Sleep(incriment_speed);
- return inc++;
- }
-
- public Int32 getIncriment()
- {
- return inc;
- }
-
- }
-
- class TestConsoleApplication
- {
-
- static void Main(string[] args)
- {
- if (args.Length<1) return;
-
- Int32 mockSpeed = 0;
- if (!Int32.TryParse(args[0], out mockSpeed)) return;
- var mock = new mockClass(mockSpeed);
-
- int beginTick = Environment.TickCount;
- for (int j = 0; j < 1200; j++)
- {
- mock.incriment();
- }
- int endTick = Environment.TickCount;
-
- var performance = new PerformanceCounter("Process", "Private Bytes", Process.GetCurrentProcess().ProcessName);
- Console.WriteLine(mock.getIncriment());
- Console.WriteLine("tick: {0}", endTick - beginTick);
- Console.WriteLine("memory: {0:N0}K", (performance.RawValue/1024));
- Console.ReadLine();
- }
- }
- }
* This source code was highlighted with Source Code Highlighter.
Запустим программу с несколькими параметрами задержки выполнения запроса: 2, 5, 10
2 | 5 | 10 | |||
tick | memory | tick | memory | tick | memory |
3688 | 10 792K | 7281 | 10 780K | 13125 | 10 792K |
Как видим память практически не страдает, а время примерно равно (mockSpeed+1)*1200. Дополнительную миллисекунду спишем на накладные расходы.
Многопоточная система
Перепишем программу для работы с многопоточностью, оптимизируем ее и сверим результаты:
- using System;
- using System.Diagnostics;
- using System.Threading;
-
- namespace TestConsoleApplication
- {
-
- class mockClass
- {
- private readonly Int32 incriment_speed;
- private Int32 inc;
-
- public mockClass(int incriment_speed)
- {
- this.incriment_speed = incriment_speed;
- inc = 0;
- }
-
- public Int32 incriment()
- {
- Thread.Sleep(incriment_speed);
- return inc++;
- }
-
- public Int32 getIncriment()
- {
- return inc;
- }
-
- }
-
- class TestConsoleApplication
- {
- private static mockClass mock = null;
-
- static void threadmethod()
- {
- lock (mock)
- {
- mock.incriment();
- }
- }
-
- static void Main(string[] args)
- {
- if (args.Length<1) return;
-
- Int32 mockSpeed = 0;
- if (!Int32.TryParse(args[0], out mockSpeed)) return;
- mock = new mockClass(mockSpeed);
-
- var performance = new PerformanceCounter("Process", "Private Bytes", Process.GetCurrentProcess().ProcessName);
- long performance_RawValue = 0;
- int beginTick = Environment.TickCount;
- lock (mock)
- {
- for (int j = 0; j < 1200; j++)
- {
- var trd = new Thread(threadmethod, 65536); //выделяем 1 страницу под стек
- trd.Start();
- }
- performance_RawValue = performance.RawValue;
- }
- int end1Tick = Environment.TickCount;
- while(mock.getIncriment()<1200)
- {
- Thread.Sleep(2);
- }
- int end2Tick = Environment.TickCount;
-
- Console.WriteLine("starttick: {0}", end1Tick - beginTick);
- Console.WriteLine("alltick: {0}", end2Tick - beginTick);
- Console.WriteLine("memory: {0:N0}K", (performance_RawValue / 1024));
- Console.ReadLine();
- }
- }
- }
* This source code was highlighted with Source Code Highlighter.
- | 2 | 5 | 10 | ||||||
- | start tick | all tick | memory | start tick | all tick | memory | start tick | all tick | memory |
Однопоточная | - | 3688 | 10 792K | - | 7281 | 10 780K | - | 13125 | 10 792K |
Многопоточная | 656 | 4234 | 323 508K | 625 | 7719 | 323 508K | 750 | 13735 | 323 508K |
При тестировании производительности многопоточности появилась новая величина время запуска процессов. Именно на эту величину вырастает общая продолжительность выполнения программы. Примерный запуск процесса равен 0,5миллисекундам. Также мы видим, значительно выросший объем используемой памяти, которая расходуется на стек запускаемых потоков.
Итоги
Выделим все сравниваемые величины в таблицу.
- | Однопоточная | Многопоточная |
Общее время | Общее время основного потока зависит от времени выполнения всех запросов | Время работы основного потока зависит только от количества запросов |
Общее процессорное время | Низкие паразитные нагрузки | Паразитные нагрузки в 2 раза выше |
Память | Невысокие запросы к памяти, независящие от количества запросов | На каждый запрос расходуется не менее 256Кб памяти на стек потока |
Вот такая «студенческая лабораторная работа» вышла при изучении такого вопроса. Прошу не кидать камни :)