Возможно я не впервые поднимаю подобную тему, но дело вот какое... Я заметил один очень удививший меня косяк в Threading-е от .NET Framework 2.0. Мне очень важны ваши, коллеги, комментарии этой ситуации. Далее подробности...
Предыстория
Так или иначе мне захотелось узнать, сколько потоков нараз может создать-запустить процесс, работающий под .NET Framework. Для этого я написал незамысловатую программку, которая создаёт и запускает потоки до упора — до тех пор, пока не возникнет какая-либо исключительная ситуация. Условия были взяты за идеальные, поэтому сам поток никакой логикой я не стал нагружать и лишь только инкрементировал в нём счётчик запустившихся потоков, а затем ставил на минуту в ожидание. Код программки получился вот такой:
Эту программку я откомпилировал под фреймворки версий 1.1 и 2.0 (прижелании бинарники можно найти здесь).
Собственно история
Эти программки я прогнал не только на нескольких доступных мне системах, но и попросил сделать то же самое нескольких своих приятелей. И собранные в итоге сведения меня очень-очень поразили.
Для фреймворка 1.1 количество созданных и запущенных потоков составило в среднем примерно 1950, вне зависимости от системы (Windows 2000/XP/Vista) и конфигурации аппарата. Мощность железки влияла лишь на время выполнения всего этого цикла, и оно в любом случае было крайне мало. Во всех случаях всплеск по общему потреблению оперативной памяти составил примерно 50 Мб, и на работу системы ощутимого влияния выполнение этого процесса не оказывало. Результаты, надо сказать, достаточно неплохие.
Но результаты, полученные для фреймворка 2.0, сложно назвать утешительными. Для него количество потоков в большинстве случаев получилось примерно 1500, но в целом ряде случаев получались и такие результаты, как ~800 потоков или ~1900 потоков. Зависело это напрямую от количества доступной оперативной памяти. Время выполнения цикла в большинстве случаев (при количестве 1500 потоков) было в два раза больше, чем для FX 1.1. Но самое поразительное то, что в момент выполнения процесса всплеск по общему потреблению памяти был просто колоссальным. Соответствующий график в Task Manager уходил в потолок, и собственно по достижении этого потолка цикл и завершался. Практически во всех случаях система выдавала сообщение, что память всё. Кроме того исполнение этого процесса на не шибко мощных машинах просто переклинивало систему, и её отпускало лишь по завершению программы, когда все потоки выходили из ожидания и завершались. Самое интересно, что исключением в этой ситуации стала Windows Vista, под которой честно выделялись ~1450 потоков, а всплеск по общему потреблению памяти был даже чуть меньше, чем для FX 1.1, и составил примерно 30 Мб.
В итоге полученные результаты отбили у меня желание портировать под FX 2.0 разработанный мною процессинговый сервис, успешно работавший на высоких нагрузках под FX 1.1.
ES>Но результаты, полученные для фреймворка 2.0, сложно назвать утешительными. Для него количество потоков в большинстве случаев получилось примерно 1500, но в целом ряде случаев получались и такие результаты, как ~800 потоков или ~1900 потоков.
Windows 2003 64 bit, .net 2.0, 2 Gb RAM:
Threads created: 3362
Time elapsed: 00:00:01.0312500
С учетом, что в системе в районе 4 Гб виртуальной памяти, получается примерно по 1.5 Мб на поток. Фактически, это размер стека, который выделяется для этого потока.
Потенциал Threading в FX 1.1 и FX 2.0
От:
Аноним
Дата:
29.11.06 01:41
Оценка:
в 2.0 больше удобства и больше тормазов , а ты не пробывал уменьшить стек для создаваемого потока? или оптимизировать архитектуру, по всей видимости у тебя приложение с блокирующими сокетами, попробуй покопать в сторону асинхронии, или в пулы потоков.
Абсолютно идиотский тест. Количество потоков не должно превышать количество физических процессоров больше чем в два раза. Серверный софт с с более чем 4-5 потоками на процессор в топку.
"Серверный софт" тот самый как раз ThreadPool использует
А тест чисто на оценку потенциальных возможноестей... Ясное дело, что если в потоке будет более-менее навороченная логика, то тупо создавать-запускать потоки с нею не дело.
Здравствуйте, e-Shaman, Вы писали:
ES>В итоге полученные результаты отбили у меня желание портировать под FX 2.0 разработанный мною процессинговый сервис, успешно работавший на высоких нагрузках под FX 1.1.
Довольно странное решение -- использовать в сервере, где возникают выскокие нагрузки, сотни и даже тысячи потоков. Это же не fibers, threads банально не предназначены для подобного использвания. А уж сколько там памяти кто кушает -- это дело вторичное, ошибка была допушена раньше при дизайне системы.
Уххх! Да не использует мой сервер этот подход! Он использует гибридный вариант на основе ThreadPool в качестве основной очереди для массового запуска потоков, и отдельных Thread-ов как дополнительных ресурсов для исполнения задач с очень большим временем исполнения, чтобы не захламлять ими ThreadPool.
Идея провести тот тест возникла из чисто спортивного интереса. Это вот как прыжки в высоту с шестом или как какой-нибудь Street Racing, когда люди которые, в обыденной ситуации передвигаются пешком или на авто в более-менее вменяемом режиме, хотят раскрыть свой потенциал или потенциал движка своего автомобиля и т.п. Слово "потенциал" и тут, и в случае с Threading является колючевым. Давайте тогда будем называть наш случай Thread Racing...
Здравствуйте, e-Shaman, Вы писали:
ES>Уххх! Да не использует мой сервер этот подход! Он использует гибридный вариант на основе ThreadPool в качестве основной очереди для массового запуска потоков, и отдельных Thread-ов как дополнительных ресурсов для исполнения задач с очень большим временем исполнения, чтобы не захламлять ими ThreadPool.
Тогда не вижу проблем с портабельностью.
ES>Идея провести тот тест возникла из чисто спортивного интереса. Это вот как прыжки в высоту с шестом или как какой-нибудь Street Racing, когда люди которые, в обыденной ситуации передвигаются пешком или на авто в более-менее вменяемом режиме, хотят раскрыть свой потенциал или потенциал движка своего автомобиля и т.п.
Мое ИМХО -- это примерно как устраивать соревнование, в какой OS можно больше окон одновременно создать. Так сказать, чтобы раскрыть потенциал User32 Практическая польза близка к нулю.
Практический вывод тут как минимум такой — FX 1.1 создаёт и запускает потоки не только быстрее, чем FX 2.0, но и аккуратнее, о чём свидетельствует примерно одинаковое количество максимума этих потоков для разных систем и затрачиваемый при этом объём оперативной памяти.
Поэтому если говорить про стабильность и надёжность работы севрверного решения на основе .NET Framework, то тут всё скорее в пользу старого доброго 1.1.
Да, я полагаю, можно портировать мой сервис на 2.0, что я возможно и сделаю когда-нибудь, но пока овчинка не стоит выделки, поэтому не вижу плюсов за то, чтобы это делать сейчас, или если сейчас, то запускать такое решение в production, когда такая картинка по нагрузочным характеристикам Threading-а.
Ладно, похоже, что спорить быссмысленно, и каждый останется при своем мнении. Естественно, если что-то уже хорошо работает, то нет никакой необходимостьи его ломать и куда-то переносить. Но делать выводы о надежности чего-либо исходя из столь странных и нетипичных тестов я бы не стал
Здравствуйте, e-Shaman, Вы писали:
ES>Ну почему же?..
ES>Практический вывод тут как минимум такой — FX 1.1 создаёт и запускает потоки не только быстрее, чем FX 2.0, но и аккуратнее, о чём свидетельствует примерно одинаковое количество максимума этих потоков для разных систем и затрачиваемый при этом объём оперативной памяти.
Мягко говоря, странный вывод.
Напоминает логику тех, кто измеряет качество операционной системы размером ее ядра в памяти...
Тебе вроде абсолютно конкретно ткнули, что конкретно измеряет твой тест. А измеряет он размер стека, выделяемого потоку по-умолчанию.
Тебе нужно создать 10000 потоков? Уменьшай размер стека. Тебе это не нужно? Правильно, никому не нужно.
Скажи на милость, а каким образом твой тест доказывает, что если потоку выделить меньший размер стека, то он будет работать "не только быстрее, но и аккуратнее"? Я так думаю, что все в точности наоборот. Поэтому во втором FW это поведение и изменили.
Mab>Ладно, похоже, что спорить быссмысленно, и каждый останется при своем мнении. Естественно, если что-то уже хорошо работает, то нет никакой необходимостьи его ломать и куда-то переносить. Но делать выводы о надежности чего-либо исходя из столь странных и нетипичных тестов я бы не стал
Ок. если по вашему мнению этот тест является "странным и нетипичным",
тогда приведите пожалуйста простой стресс тест как можно сравнить .NET 1.1 и 2.0 в
плане многопоточности и потребляемой при этом памяти/загрузки CPU.
Я думаю многим будет интересно.
Здравствуйте, e-Shaman, Вы писали:
ES>Бррр ES>Я вообще как-то и не затрагивал особо тему размера стека, поэтому с этим в этой веточке, наверное, не ко мне...
ES>Кстати, у вас есть какое-то определённое представление о том, как это всё работает, как функционирует это самое поведение?
Память выжирается и все падает в зависимости от размера стека. Твой тест, по сути, проверяет, какой объем памяти под это дело выделяется в разных версиях фреймворка. Не больше — не меньше.
Т.е. из твоего теста можно сделать только такой вывод, что теперь размер стека по-умолчанию увеличен. И никакого другого.
Цитирую MSDN:
The ability to set a thread's stack size is not new to Win32® developers, as the CreateThread function exposes that capability, but it is new to managed threads with the .NET Framework 2.0. Just because the option is exposed doesn't mean you should use it, though. The default stack size of 1MB should be appropriate for almost any application you're developing. If it's not, chances are you have a bug or poor design in your code. That said, there are valid circumstances where you might want to change the stack size.
An easy way to see the effect of this parameter is to create and start as many threads as you can. Suppose you have two gigabytes of virtual address space. If all of that space is available for thread creation and if each thread reserves 1MB of memory, you should be able to create around 2000 threads. Figure 2 shows a simple test harness for counting the number of threads that can be created and started with a specific stack size. Sure enough, calling it three times with starting stack sizes of 512KB, 1MB, and 2MB, respectively, yields the following output:
After 3828 threads: OutOfMemoryException
After 1917 threads: OutOfMemoryException
After 956 threads: OutOfMemoryException
Здравствуйте, Jericho113, Вы писали:
J>Я думаю многим будет интересно.
Наверное будет, но только это сложная задача, за которую я не собираюсь браться. Даже выработать объективный параметр эффективности тут непросто.
Здравствуйте, e-Shaman, Вы писали:
ES>Возможно я не впервые поднимаю подобную тему, но дело вот какое... Я заметил один очень удививший меня косяк в Threading-е от .NET Framework 2.0. Мне очень важны ваши, коллеги, комментарии этой ситуации. Далее подробности...
А какие комментарии тут можно сделать? Нужно отдавать себе отчет в том, что Thread как таковой является весьма низкоуровневым средством. Его нужно правильно использовать.
Возможность запускать много потоков одновременно, собственно говоря, нужна для обеспечения рационального использования аппаратуры. Из интуитивных соображений ясно, что приложение с 1 потоком никогда не сможет задействовать несколько CPU одновременно.
Физически, конечно же, никакая машина не сможет реально одновременно исполнять больше потоков, чем в ней CPU.
Однако это вовсе не означает, что нет смысла в создании более чем 2х потоков. Поток — это единица планирования операционной системы. Сама структура вынуждает планировщик быть очень тупым; у него нет практически никакой информации о смысле и цели потоков. Все, что у него есть — это приоритет потока и состояния блокировок.
Работу большинства приложений можно разделить на некие, скажем так, активности (work item). Они могут быть большими и маленькими, могут включать работу с RAM, CPU, HDD, сетью и т.п. Ну, например "обработать клик", "обработать HTTP запрос", "скопировать файл" и т.п. Как я уже говорил, одновременно может выполняться лишь небольшое количество активностей. Все остальные будут вынуждены ждать момента, когда освободятся необходимые ресурсы.
Можно поставить в соответствие 1 активность = 1 поток. Но это довольно дорогостоящее удовольствие; почти что как "одно окно — один монитор". Потому, что
а) даже те потоки, которые спят в ожидании какого-то события, увеличивают объем ынутренних таблиц шедулера, замедляя его процесс принятия решений
б) потоки расходуют системные ресурсы. В первую очередь, каждому потоку выделяется некоторый объем памяти под стек.
Поэтому такой наивный подход применим только для тех случаев, когда активностей в приложении немного и они крупномасштабные.
Для приложений массового обслуживания надо помогать шедулеру. В частности, если заранее известно, что какой-то активности не нужен мегабайт стека, то можно подкрутить настройки потока, резко снизив потребление ресурсов.
Но это только начало. Допустим, мы хотим выполнить 2000 каких-то задачек. При этом мы прикидываем, что одновременно не смогут выполняться более 20. В таком случае, создав для них сразу 2000 потоков, мы вынудим систему отложить нам большое количество ресурсов, которые гарантированно не понадобятся в течение долгого времени.
В свете этого становится ясно, что рассуждения о максимальном количестве ОС потоков, которые можно одновременно запустить, большого смысла не имеют. Не бывает таких приложений, которым нужно 2000 потоков одновременно. 2000 активностей — да. Но только в том случае, если это "ленивые" активности. Например, приложения сетевого обмена. Там все просто: отправив или приняв пачку байт, можно спать бесконечное по меркам CPU время. Тратить целый поток на этот сон разума смысла не имеет. Имеет смысл поставить перед этим потоком новую задачу.
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, e-Shaman, Вы писали:
A>Абсолютно идиотский тест. Количество потоков не должно превышать количество физических процессоров больше чем в два раза. Серверный софт с с более чем 4-5 потоками на процессор в топку.
Прежде чем такую чушь пороть посмотри хотя бы сколько поток в процессах Exchange или lsass.
Здравствуйте, adontz, Вы писали:
A>Абсолютно идиотский тест. Количество потоков не должно превышать количество физических процессоров больше чем в два раза. Серверный софт с с более чем 4-5 потоками на процессор в топку.
обоснуйте на чем основаны ваши слова?
На мой взгляд серьезный серверный софт требует достаточно сложных механизмов синхронизации, поэтому обойтись 4-5 потоками будет крайне сложно, разумеется потоки на каждого клиента клепать глупо, но вот держать свой ThreadPool потоки которого обрабатывают клиентов очень удобно.
Спасибо, коллега!
Очень хорошие вещи вы говорите...
Я вот тут уже тоже думаю, что я погорячился с негативом в адрес FX 2.0, и уже думаю, что можно достаточно эффективно заюзать фичу с рулением размером стека, чего не было в FX 1.1. Плюс есть приоритеты, а ещё и собственная очередь ожидания, откладывающая момент реального создания потока, про которую вы говорите.
Так вот я думаю, что если накапливать историю выполнения различных типов активностей, выраженную в каких-то числовых величинах, к которым применимо понятия матожидания, да и вообще какие-нибудь методы прогнозирования, то, опираясь на эти величины, можно управлять параметрами запуска реального потока. При достаточно грамотной реализации это может послужить как раз той самй помощью системе, про которую вы говорите...
Да слова-то, в общем-то, верные. Кол-во активных потоков нужно держать минимальным (пропорционально числу процессоров) и использовать pool. А new Thread() к этому никакого отношения не имеет. Так что тест, основанный на создании огромного кол-ва потоков таким образом, действительно не показательный.
Здравствуйте, Morpheus_, Вы писали:
M_>На мой взгляд серьезный серверный софт требует достаточно сложных механизмов синхронизации, поэтому обойтись 4-5 потоками будет крайне сложно, разумеется потоки на каждого клиента клепать глупо, но вот держать свой ThreadPool потоки которого обрабатывают клиентов очень удобно.
Надо писать statefull finite machine и никаких проблем вида "нехватает потоков" не возникает в принципе. Кстати большинство RFC сетевых протоколов описывают сервер именно в виде конечного автомата оперируя терминами state и command.
Здравствуйте, Morpheus_, Вы писали:
M_>нафик тогда многозадачность нужна, если судя по этим словам самый эффективный сервер это сервер использующий не более 1-го потока...
Текс. Учим матчасть. Переключение потоков не бесплатная операция. Если у тебя более одного реально что-то делающего потока на процессор, то время тратится не только на работу, но и на переключение.
А многозадачность она в целом нужна не между потоками, а между процессами (между потоками внутри процесса можно даже врукопашную переключение сделать), что уже открывает совсем другие возможности.
Здравствуйте, adontz, Вы писали:
A>Текс. Учим матчасть. Переключение потоков не бесплатная операция. Если у тебя более одного реально что-то делающего потока на процессор, то время тратится не только на работу, но и на переключение. A>А многозадачность она в целом нужна не между потоками, а между процессами (между потоками внутри процесса можно даже врукопашную переключение сделать), что уже открывает совсем другие возможности.
Прежде чем рекоммендовать кому-либо учить матчасть, следует ее и самому хотя бы немножко знать. Представь что к серверу поступают два типа запросов: первый тип требует обращения к диску, а второй — чисто вычислительный. К серверу пришел запрос первого типа, который потребует относительно большого времени для ожидания завершения операции с диском. Почему бы это время не использовать с умом и не обслужить запрос второго типа?
Здравствуйте, Lloyd, Вы писали:
L>Прежде чем рекоммендовать кому-либо учить матчасть, следует ее и самому хотя бы немножко знать. Представь что к серверу поступают два типа запросов: первый тип требует обращения к диску, а второй — чисто вычислительный. К серверу пришел запрос первого типа, который потребует относительно большого времени для ожидания завершения операции с диском. Почему бы это время не использовать с умом и не обслужить запрос второго типа?
Вот всё твоё сообщение почему-то исходит из ложной предпосылки первичности синхронного ввода-вывода. На уровне драйверов никто никого не ждёт, используются Completion Handlers в той или иной форме. Так что ты предлагаешь сперва создать систему ожидайния, а потом много потоков, чтобы они пользовались этой системой и тупо ждали. А на самом деле рулит асинхронный ввод-вывод.
Здравствуйте, adontz, Вы писали:
L>>Прежде чем рекоммендовать кому-либо учить матчасть, следует ее и самому хотя бы немножко знать. Представь что к серверу поступают два типа запросов: первый тип требует обращения к диску, а второй — чисто вычислительный. К серверу пришел запрос первого типа, который потребует относительно большого времени для ожидания завершения операции с диском. Почему бы это время не использовать с умом и не обслужить запрос второго типа?
A>Вот всё твоё сообщение почему-то исходит из ложной предпосылки первичности синхронного ввода-вывода. На уровне драйверов никто никого не ждёт, используются Completion Handlers в той или иной форме. Так что ты предлагаешь сперва создать систему ожидайния, а потом много потоков, чтобы они пользовались этой системой и тупо ждали. А на самом деле рулит асинхронный ввод-вывод.
Ну да, рулит. Но путем серьезного увеличения сложности кода.
Здравствуйте, Morpheus_, Вы писали:
M_>Здравствуйте, mrozov, Вы писали:
M>>Кол-во активных потоков нужно держать минимальным (пропорционально числу процессоров)
M_>можно поподробнее откуда появилось это "пропорционально числу процессоров"?
Можно.
Помимо здравого смысла (только один активный поток на процессор имеет смысл, остальные просто ждут своей очереди) есть книга Рихтера о серверном программировании. Очень рекомендую.
M_>нафик тогда многозадачность нужна, если судя по этим словам самый эффективный сервер это сервер использующий не более 1-го потока...
Многозадочность нужно использовать с умом. Сама по себе она ничего не дает, но многое отнимает. Как минимум — ресурсы на каждый поток + время на переключение.
Реальный смысл в многопоточности появляется только тогда, когда потоки часть времени либо спят в ожидании события, либо занимаются вводом/выводом. Но и в том и в другом случае намного эффективнее использовать пул потоков. Причем .net-овский ThreadPool в этом отношении, мягко говоря, не идеален, но все равно лучше, чем создание потоков "с нуля".
В конце концов — процессор физически не может обрабатывать несколько потоков одновременно. HT и мультиядерность в расчет не берем.
Но в любом случае — автор сам признал, что его тест неэффективен. Он просто меряет размер стека, выделяемый потоку по-умолчанию. О чем тут еще говорить-то?
Здравствуйте, adontz, Вы писали:
M_>>нафик тогда многозадачность нужна, если судя по этим словам самый эффективный сервер это сервер использующий не более 1-го потока...
A>Текс. Учим матчасть. Переключение потоков не бесплатная операция.
после того как я написал пару RTOS ядер с гарантированным временем реакции, для меня это не новость
A>Если у тебя более одного реально что-то делающего потока на процессор, то время тратится не только на работу, но и на переключение. A>А многозадачность она в целом нужна не между потоками, а между процессами (между потоками внутри процесса можно даже врукопашную переключение сделать), что уже открывает совсем другие возможности.
и сколько же времени тратится на переключение? А сколько уйдет на проектирование машины состояний? Вы серьезно думаете что код использующий машину состояний легко читать и модифицировать, а следовательно и легко сделать эффективным?
Здравствуйте, adontz, Вы писали:
L>>Ну да, рулит. Но путем серьезного увеличения сложности кода.
A>Ну насчёт серьёзного ты загнул. Всего-то конечный автомат написать.
ну да, а потом чтоб какуюто функцию добавить сидеть разбираться в нем и писать его заново
Здравствуйте, mrozov, Вы писали:
M>>>Кол-во активных потоков нужно держать минимальным (пропорционально числу процессоров)
M_>>можно поподробнее откуда появилось это "пропорционально числу процессоров"? M>Можно. M>Помимо здравого смысла (только один активный поток на процессор имеет смысл, остальные просто ждут своей очереди) есть книга Рихтера о серверном программировании. Очень рекомендую.
речь идет про многоядерные процессоры и HT? или про Z80?
M_>>нафик тогда многозадачность нужна, если судя по этим словам самый эффективный сервер это сервер использующий не более 1-го потока... M>Многозадочность нужно использовать с умом. Сама по себе она ничего не дает, но многое отнимает. Как минимум — ресурсы на каждый поток + время на переключение.
M> M>В конце концов — процессор физически не может обрабатывать несколько потоков одновременно. HT и мультиядерность в расчет не берем.
отчего же так упорно избегать многоядерные процы и процы с HT, сейчас ведь все такие...
M>Но в любом случае — автор сам признал, что его тест неэффективен. Он просто меряет размер стека, выделяемый потоку по-умолчанию. О чем тут еще говорить-то?
речь уже идет не о тесте, а о высказывании "использование на сервере более 1 потока на процессор — ф топку"
Коллега, я что-то не пойму, вы ведете спор ради спора? Я в таких не участвую, мне это не интересно.
HT и мультиядерность в данном контексте просто является аналогом использования нескольких процессоров, вот и все. Я их игнорирую для упрощения терминологии.
Если по существу комментариев нет, то предлагаю на этом дискуссию завершить. Вы задали вопрос "откуда это взялось", я вам ответил, тема по-идее должна быть изчерпана.
P.S. Я-то многопоточные серверные приложения пишу регулярно, работа у меня такая. Рихтер у меня любимый писатель после Чехова. Темой в целом владею.
Здравствуйте, mrozov, Вы писали:
M>Коллега, я что-то не пойму, вы ведете спор ради спора? Я в таких не участвую, мне это не интересно. M>HT и мультиядерность в данном контексте просто является аналогом использования нескольких процессоров, вот и все. Я их игнорирую для упрощения терминологии.
M>Если по существу комментариев нет, то предлагаю на этом дискуссию завершить. Вы задали вопрос "откуда это взялось", я вам ответил, тема по-идее должна быть изчерпана.
Здравствуйте, e-Shaman, Вы писали:
ES>Ну почему же?..
ES>Практический вывод тут как минимум такой — FX 1.1 создаёт и запускает потоки не только быстрее, чем FX 2.0, но и аккуратнее, о чём свидетельствует примерно одинаковое количество максимума этих потоков для разных систем и затрачиваемый при этом объём оперативной памяти.
ES>Поэтому если говорить про стабильность и надёжность работы севрверного решения на основе .NET Framework, то тут всё скорее в пользу старого доброго 1.1.
не убедительный вывод, например фреймворк 1.1 не поддерживает критическую финализацию, что в частности может грозить неприятностями при DoS атаках...
Здравствуйте, Morpheus_, Вы писали:
M_>и сколько же времени тратится на переключение?
Если по одному рабочему потоку на процессор, то вообще не тратится
M_>А сколько уйдет на проектирование машины состояний?
А зачем её проектировать вручную?
M_>Вы серьезно думаете что код использующий машину состояний легко читать и модифицировать, а следовательно и легко сделать эффективным?
Да, реализация ввода-вывода в виде конечного автомата не только более эффективна в плане производитености, но и в конечном итоге легче поддерживается. Говорю по собственному опыту.
Здравствуйте, adontz, Вы писали:
M_>>и сколько же времени тратится на переключение?
A>Если по одному рабочему потоку на процессор, то вообще не тратится
Чтото я еще не видел Windows работающий с одним потоком...
M_>>А сколько уйдет на проектирование машины состояний?
A>А зачем её проектировать вручную?
вопрос в том что если задача достаточно сложная, то разобраться чтобы внести какието изменения тяжело, да и осмыслить всю цепочку выполнения тоже...
M_>>Вы серьезно думаете что код использующий машину состояний легко читать и модифицировать, а следовательно и легко сделать эффективным?
A>Да, реализация ввода-вывода в виде конечного автомата не только более эффективна в плане производитености, но и в конечном итоге легче поддерживается. Говорю по собственному опыту.
я видел другой сценарий, когда нужно было внести изменения появлялись костыли, в конце концов все было так запущено что сама мысль о том что нужно разобраться что там происходит вызывала дикий ужас
Здравствуйте, Morpheus_, Вы писали:
A>>А зачем её проектировать вручную? M_>вопрос в том что если задача достаточно сложная, то разобраться чтобы внести какието изменения тяжело, да и осмыслить всю цепочку выполнения тоже...
Вообще-то она неплохо решается в полуавтоматическом режиме. Существует куча программ-генераторов конечных автоматов по некоторому описанию.
M_>я видел другой сценарий, когда нужно было внести изменения появлялись костыли, в конце концов все было так запущено что сама мысль о том что нужно разобраться что там происходит вызывала дикий ужас
Здравствуйте, Morpheus_, Вы писали:
A>>Ну насчёт серьёзного ты загнул. Всего-то конечный автомат написать. M_>ну да, а потом чтоб какуюто функцию добавить сидеть разбираться в нем и писать его заново
Здравствуйте, adontz, Вы писали:
A>>>А зачем её проектировать вручную? M_>>вопрос в том что если задача достаточно сложная, то разобраться чтобы внести какието изменения тяжело, да и осмыслить всю цепочку выполнения тоже...
A>Вообще-то она неплохо решается в полуавтоматическом режиме. Существует куча программ-генераторов конечных автоматов по некоторому описанию.
видел, пользовал, редактировать то диаграмму состояний всеравно надо, да и код генерится не достаточно эффективный
Здравствуйте, Morpheus_, Вы писали:
M_>видел, пользовал, редактировать то диаграмму состояний всеравно надо, да и код генерится не достаточно эффективный
Здравствуйте, mrozov, Вы писали:
M>P.S. Я-то многопоточные серверные приложения пишу регулярно, работа у меня такая. Рихтер у меня любимый писатель после Чехова. Темой в целом владею.
Кстати по поводу Рихтера, полностью согласен с Олег Михайлик, Рихтер копает как ускорить переключение задач и т.п., хотя на самом деле это не является проблемой, а люди начитавшись Рихтера считают что переключение задач это и есть проблема многопоточности На деле это не так, переключение задач занимает не так уж и много времени чтобы уделять этому такое пристальное внимание
Здравствуйте, adontz, Вы писали:
M_>>видел, пользовал, редактировать то диаграмму состояний всеравно надо, да и код генерится не достаточно эффективный
A>Да ну, чего это switch вдруг стал не эффективным?
если уж об этом говорить, тогда не switch, а большое кол-во call и других джампов, которые действительно пожирают время и я думаю не меньше чем переключение потоков
и код читается тяжело
Здравствуйте, Morpheus_, Вы писали:
M_>Кстати по поводу Рихтера, полностью согласен с Олег Михайлик, Рихтер копает как ускорить переключение задач и т.п., хотя на самом деле это не является проблемой, а люди начитавшись Рихтера считают что переключение задач это и есть проблема многопоточности На деле это не так, переключение задач занимает не так уж и много времени чтобы уделять этому такое пристальное внимание
Не надо сводить переключение потоков к 300 тикам загрузки TSS. Разные потоки обращаются к разным данным, а это перезагрузка кучи разных кешей, от L2 до дискового. Так что вообще говоря переключение между что-то делающими потоками это довольно дорогая операция.
Здравствуйте, Morpheus_, Вы писали:
M_>если уж об этом говорить, тогда не switch, а большое кол-во call и других джампов, которые действительно пожирают время и я думаю не меньше чем переключение потоков M_>и код читается тяжело
Ну-ну. Ты ещё скажи что надо от виртуальных методов отказыватся. Пашутил, панимаешь. CALL/JMP это пара тиков, загрука TSS около 300. Ну и я рядом уже ответил, что этими тремья стами дело не ограничивается.
Здравствуйте, adontz, Вы писали:
M_>>если уж об этом говорить, тогда не switch, а большое кол-во call и других джампов, которые действительно пожирают время и я думаю не меньше чем переключение потоков M_>>и код читается тяжело
A>Ну-ну. Ты ещё скажи что надо от виртуальных методов отказыватся. Пашутил, панимаешь. CALL/JMP это пара тиков, загрука TSS около 300. Ну и я рядом уже ответил, что этими тремья стами дело не ограничивается.
ты забыл о том что call/jmp потенциально может сбить кэш что заставит процессор остановиться до поступления данных с кодом (на который произведен jmp) из памяти в кэш, а это очень не быстрая операция
Здравствуйте, adontz, Вы писали:
M_>>Кстати по поводу Рихтера, полностью согласен с Олег Михайлик, Рихтер копает как ускорить переключение задач и т.п., хотя на самом деле это не является проблемой, а люди начитавшись Рихтера считают что переключение задач это и есть проблема многопоточности На деле это не так, переключение задач занимает не так уж и много времени чтобы уделять этому такое пристальное внимание
A>Не надо сводить переключение потоков к 300 тикам загрузки TSS. Разные потоки обращаются к разным данным, а это перезагрузка кучи разных кешей, от L2 до дискового. Так что вообще говоря переключение между что-то делающими потоками это довольно дорогая операция.
Windows изначально не однопоточная система, так что переключения задач будут происходить в любом случае, хотите вы этого или нет...
M_>ты забыл о том что call/jmp потенциально может сбить кэш что заставит процессор остановиться до поступления данных с кодом (на который произведен jmp) из памяти в кэш, а это очень не быстрая операция
процессоры, не смотря на наличие хитрых блоков предсказания, не любят call/jmp и мстят за их использование низкой производительностью
Здравствуйте, Morpheus_, Вы писали:
M_>>ты забыл о том что call/jmp потенциально может сбить кэш что заставит процессор остановиться до поступления данных с кодом (на который произведен jmp) из памяти в кэш, а это очень не быстрая операция
M_>процессоры, не смотря на наличие хитрых блоков предсказания, не любят call/jmp и мстят за их использование низкой производительностью
вобщем приходим к выводу что серверы лучше всего делать без Windows, без многозадачных ядер, с исполнением кода в одном потоке и линейным кодом без всяких джампов, функций, подпрограмм и т.п.
а вообще спор глупый, я согласен что чем меньше потоков в сервере тем лучше, но на практике получается что чем меньше потоков тем более зубодробильным получается код и больше времени уходит на его разработку. Пожалуй для сферического сервера с одним потоком код этот наверняка вызовет рак мозка
M_>не убедительный вывод, например фреймворк 1.1 не поддерживает критическую финализацию, что в частности может грозить неприятностями при DoS атаках...
Просветите что есть критическая финализация и чем эта функциональность помогает при DoS атаках..., если не секрет в двух словах будет достаточно ?
Здравствуйте, Jericho113, Вы писали:
J>Здравствуйте, Morpheus_, Вы писали:
M_>>не убедительный вывод, например фреймворк 1.1 не поддерживает критическую финализацию, что в частности может грозить неприятностями при DoS атаках...
J>Просветите что есть критическая финализация и чем эта функциональность помогает при DoS атаках..., если не секрет в двух словах будет достаточно ?
критическая финализация гарантирует вызов вызов деструктора для классов хранящих unmanaged хэндлы, при большой нагрузке на систему без критической финализации будет происходить утечка хэндлов, что достаточно быстро приведет к падению приложения или всей Win
Hello, "Morpheus_" > > критическая финализация гарантирует вызов вызов деструктора для классов > хранящих unmanaged хэндлы, при большой нагрузке на систему без критической > финализации будет происходить утечка хэндлов, что достаточно быстро > приведет к падению приложения или всей Win
А можно по подробнее про критическую финализацию?
В MSDN написано следующее:
In classes derived from the CriticalFinalizerObject class, the common
language runtime (CLR) guarantees that all critical finalization code will
be given the opportunity to execute, provided the finalizer follows the
rules for a CER, even in situations where the CLR forcibly unloads an
application domain or aborts a thread. If a finalizer violates the rules
for a CER, it might not successfully execute. In addition, the CLR
establishes a weak ordering among normal and critical finalizers: for
objects reclaimed by garbage collection at the same time, all the
noncritical finalizers are called before any of the critical finalizers.
Первый выделенный фрагмент говорит о том, что критический финализатор будет
выполнен даже в случае "не нормального" завершения приложения. С другой
стороны, это никак не соотносится с ситуацией, если приложение не должно
падать "в принципе". Второй выделенный фрагмент говорит о том, что
"критические финализаторы" будут отрабатывать после "не критических" т.е.
фактически, в случае DDoS атаки критические финализаторы будут последними в
очереди "на очистку". Что не соотносится с утверждением, что критический
финализатор это панацея от "утечки хендлов" в случае DDoS атаки. Более того,
они ей только способствуют.
Posted via RSDN NNTP Server 2.0
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
TK>objects reclaimed by garbage collection at the same time, all the
TK>noncritical finalizers are called before any of the critical finalizers.
TK>Первый выделенный фрагмент говорит о том, что критический финализатор будет TK>выполнен даже в случае "не нормального" завершения приложения. С другой TK>стороны, это никак не соотносится с ситуацией, если приложение не должно TK>падать "в принципе". Второй выделенный фрагмент говорит о том, что TK>"критические финализаторы" будут отрабатывать после "не критических" т.е. TK>фактически, в случае DDoS атаки критические финализаторы будут последними в TK>очереди "на очистку". Что не соотносится с утверждением, что критический TK>финализатор это панацея от "утечки хендлов" в случае DDoS атаки. Более того, TK>они ей только способствуют.
хм... предположу что благодаря тому что CLR не гарантирует вызов обычных финализаторов, CLR может со спокойной совестью забить на обычные и начать вызывать критические...
На практике не пробовал, исключительно мое предположение
Здравствуйте, Morpheus_, Вы писали:
M_>хм... предположу что благодаря тому что CLR не гарантирует вызов обычных финализаторов, CLR может со спокойной совестью забить на обычные и начать вызывать критические... M_>На практике не пробовал, исключительно мое предположение
На обычные финализаторы CLR может забить только в случае завершения работы приложения.
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Здравствуйте, adontz, Вы писали:
A>>>Ну насчёт серьёзного ты загнул. Всего-то конечный автомат написать. M_>>ну да, а потом чтоб какуюто функцию добавить сидеть разбираться в нем и писать его заново A>Вы их руками пишите? Мнда...
А есть автоматизированные средства с генерацией быстрого кода под C# ?
Я не прикалываюсь — мне правда интресно.
Читал про пару подходов — но методика вроде "все через делегаты" смущает в плане быстродействия.
Здравствуйте, TK, Вы писали: TK>На обычные финализаторы CLR может забить только в случае завершения работы приложения.
Ну то есть сначала переполнение очереди критической финализации приведет к утечке ресурсов, которая вызовет падение приложения, а уже потом дотнет бросит заниматься обычными финализаторами и вернется к критическим
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Nikolay_P_I, Вы писали:
N_P>А есть автоматизированные средства с генерацией быстрого кода под C# ? N_P>Я не прикалываюсь — мне правда интресно. N_P>Читал про пару подходов — но методика вроде "все через делегаты" смущает в плане быстродействия.
Я на такие вещи не заморачиваюсь. Без профайлера это всё девичьи страхи.
"Вах-вах-вах, у нас вызов функции, это же так дорого!" Ну и фиг с ним, главное чтобы работало и легко поддерживалось, а оптимизировать количество вызов функций это вообще не для языка типа C# задача. В .Net куча build-in тормозов гораздо заметнее, чем вызов функции. Например все линейные контейнеры и массивы проверяют корректность индекса, а это два if. И чё теперь, писать свои unchecked контейнеры? Не надо спускатся до несущественных мелочей, надо мыслить более обще, категориями более высокого уровня.
Ввод-вывод (сетевой, файловый) по сути своей операция асинхронная, поэтому выгодно после инициации не ждать завершения, а указать обработчик который будет вызван после завершения операции. Переключение потока по сравнению с запросом к БД операция конечно же дешёвая, но на поток тратятся ресурсы. Если у тебя 2 процессора, то 2 рабочих потока это замечательно, 4 норма, 8 терпимо, а если у тебя 100 рабочих потоков, то это уже бардак, они будут постоянно друг другу мешать перезагружая кеши. Потому и делают пул потоков в контексте которых и вызываются обработчики завершения ввода-вывода. А если уже сделал пул, то конечно же ограничиваешь его размер линейной функцией от количества процессоров, потому что вобщем-то пофиг как ограничивать его размер, но так правильнее.
Здравствуйте, adontz, Вы писали:
A>Здравствуйте, Nikolay_P_I, Вы писали:
N_P>>А есть автоматизированные средства с генерацией быстрого кода под C# ? N_P>>Я не прикалываюсь — мне правда интресно. N_P>>Читал про пару подходов — но методика вроде "все через делегаты" смущает в плане быстродействия.
A>Я на такие вещи не заморачиваюсь. Без профайлера это всё девичьи страхи. A>"Вах-вах-вах, у нас вызов функции, это же так дорого!"
Вызов функции не слишком смущает — смущает замена всех функций на делегаты
Ну и кроме того — оно было теорией, а опять-таки — не автоматизированным средством генерации кода конечного автомата на c#.
Средства — то есть ? Хочу посмотреть и попробовать.
P.S. У меня-то — давно сплошные пулы и блокирующие сокеты
Hello, "Sinclair" > TK>На обычные финализаторы CLR может забить только в случае завершения > работы приложения. > Ну то есть сначала переполнение очереди критической финализации приведет к > утечке ресурсов, которая вызовет падение приложения, а уже потом дотнет > бросит заниматься обычными финализаторами и вернется к критическим
Да, если за собой не убирать то, примерно так все и будет Сначала CLR
престанет справляться с финализацией обычных объектов одновременно с этим
начнут заканчиваться unmanaged ресурсы, потом приложение упадет (по
какой-либо причине) и тогда, CLR плюнет на финализацию простых смертных и
займется критическими...
PS. А что значит "переполнение очереди критической финализации"?
Posted via RSDN NNTP Server 2.0
Если у Вас нет паранойи, то это еще не значит, что они за Вами не следят.
Здравствуйте, TK, Вы писали:
TK>PS. А что значит "переполнение очереди критической финализации"?
Ну, я так понял, что фразу про вызов критических после некритических можно трактовать как наличие двух очередей финализации, до которой дело доходит только после выгребания первой. Это означает, что все избежавшие диспоза критические финализаторы имеют шансы замерзнуть там в отличие от некритических, которые таки будут успевать собираться. В таком случае, ресурсами, залипшими в некритических финализаторах можно при анализе пренебречь — они будут возвращаться в систему. А вот критические, получается, будут держать хэндлы и привести к их нехватке. Если приложение не готово корректно обрабатывать исключения при конструировании хэндлов, оно и упадет.
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, adontz, Вы писали: A>Я на такие вещи не заморачиваюсь. Без профайлера это всё девичьи страхи. A>"Вах-вах-вах, у нас вызов функции, это же так дорого!" Ну и фиг с ним, главное чтобы работало и легко поддерживалось, а оптимизировать количество вызов функций это вообще не для языка типа C# задача. В .Net куча build-in тормозов гораздо заметнее, чем вызов функции. Например все линейные контейнеры и массивы проверяют корректность индекса, а это два if.
вообще-то один. И ОЧЕНЬ быстр даже если его не удается элиминировать, т.к. выполняется над регистром.
C остальным согласен.
1.2.0 alpha rev. 655
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Здравствуйте, Morpheus_, Вы писали:
A>>Если по одному рабочему потоку на процессор, то вообще не тратится M_>Чтото я еще не видел Windows работающий с одним потоком...