2010-01-12 3 views
3

Я занимался параллельной библиотекой в ​​.NET 4.0. Недавно я разработал обычную ORM для некоторых необычных операций чтения/записи, которые должна использовать одна из наших больших систем. Это позволяет мне украшать объект атрибутами и анализировать, какие столбцы он должен извлекать из базы данных, а также то, что XML должен выводить на записи.Многопоточность в .NET 4.0 и производительность

Поскольку я рассматриваю эту оболочку для повторного использования во многих проектах, я бы хотел как можно быстрее выжать из нее максимально возможную скорость. Эта библиотека будет в основном использоваться в веб-приложениях .NET. Я тестирую фреймворк, используя одноразовое консольное приложение, чтобы ткнуть в классы, которые я создал.

Теперь я извлек урок издержек, с которыми сталкивается многопоточность. Многопоточность заставляет его работать медленнее. От чтения вокруг кажется, что он интуитивно понятен людям, которые делали это в течение долгого времени, но на самом деле он интуитивно понятен мне: как можно запустить метод 30 раз в то же время быть медленнее, чем запустить его 30 раз подряд?

Я не думаю, что я вызываю проблемы несколькими потоками, которые должны сражаться за один и тот же общий объект (хотя я недостаточно хорош в этом, пока не знаю наверняка или нет), поэтому я предполагаю, что замедление исходя из накладных расходов, порождая все эти потоки и время выполнения, поддерживая их все прямо. Итак:

  • Хотя я делаю это в основном как учебное упражнение, это пессимизация? Для тривиальных задач, отличных от IO, многопоточное избыточное число? Моя главная цель - скорость, а не отзывчивость пользовательского интерфейса или что-то еще.
  • Запустил бы такой же многопотоковый код в IIS, чтобы ускорить его из-за уже созданных потоков в пуле потоков, тогда как прямо сейчас я использую консольное приложение, которое, как я полагаю, будет однопоточным, пока я не скажу в противном случае? Я собираюсь запустить некоторые тесты, но я полагаю, что есть некоторые базовые знания, которые мне не хватает, чтобы знать , почему было бы так или иначе. Мое консольное приложение также работает на моем рабочем столе с двумя ядрами, тогда как сервер для веб-приложения будет иметь больше, поэтому мне, возможно, придется использовать его как переменную.

ответ

8

Нить фактически не работает одновременно.

На настольной машине я предполагаю, что у вас двухъядерный процессор (возможно, не более четырех). Это означает, что одновременно могут работать только потоки 2/4.

Если вы создали 30 потоков, ОС будет иметь контекстный переключатель между этими 30 потоками, чтобы все они работали. Контекстные коммутаторы довольно дорогостоящие, поэтому, следовательно, замедление.

В качестве основного предложения я бы нацелился на 1 поток на процессор, если вы пытаетесь оптимизировать вычисления. Больше, чем это, и вы на самом деле не занимаетесь какой-либо дополнительной работой, вы просто меняете потоки в одном процессоре. Постарайтесь думать о том, что ваш компьютер имеет ограниченное количество рабочих внутри, вы не можете делать больше работы одновременно, чем количество рабочих, которых вы имеете.

Некоторые из новых функций в параллельной библиотеке задач .net 4.0 позволяют делать то, что учитывает масштабируемость в количестве потоков. Например, вы можете создать множество задач, а параллельная библиотека задач будет внутренне определять, сколько у вас доступных процессоров и оптимизировать количество потоков, создает/использует, чтобы не перегружать процессоры, поэтому вы могли бы создать 30 задач, но на двухъядерном компьютере библиотека TP все равно создаст только 2 потока и поставит в очередь. Очевидно, что это будет очень хорошо масштабироваться, когда вы сможете запустить его на более крупной машине.Или вы можете использовать что-то вроде ThreadPool.QueueUserWorkItem(...), чтобы поставить очередь задач, и пул будет автоматически управлять тем, сколько потоков используется для выполнения этих задач.

Да, есть много накладных расходов на создание потоков, но если вы используете пул потоков .net (или параллельную библиотеку задач в 4.0) .net будет управлять созданием потоков, и вы действительно можете найти его создает меньше потоков, чем количество созданных задач. Он будет внутренне менять ваши задачи вокруг доступных потоков. Если вы действительно хотите контролировать явное создание реальных потоков, вам нужно будет использовать класс Thread.

[Некоторые процессоры могут делать умные вещи с потоками и могут иметь несколько потоков, работающих на процессор, - см. hyperthreading - но проверьте свой диспетчер задач, я был бы очень удивлен, если у вас более 4-8 виртуальных процессоров на сегодняшнем рабочие столы]

+0

Добавление немного к тому, что сказал Саймон, оптимальное количество потоков трудная вещь, чтобы найти, как это часто зависит от нагрузки системы в целом и то, что делает ваш код, мой лучший совет - экспериментировать с высокой нагрузкой, чтобы увидеть, что дает лучшую производительность. – Lazarus

+0

@ Lazarus. Да, я полностью согласен, это очень верно. И это может быть очень специфичным для системы. Это то, что вы, вероятно, хотите каким-то образом разоблачить как настройки, чтобы его можно было настроить по отдельности, с некоторыми хорошими значениями по умолчанию. –

1

Вы говорите об ORM, поэтому я предполагаю, что некоторое количество операций ввода-вывода продолжается. Если это так, накладные расходы на создание потоков и переключение контекста будут сравнительно отсутствовать.

Скорее всего, вы столкнулись с конфликтом ввода-вывода: он может быть медленнее (особенно на вращающихся жестких дисках, но также и на других устройствах хранения), чтобы читать один и тот же набор данных, если вы читаете его не по порядку, чем если бы вы читаете его в порядке. Таким образом, если вы выполняете 30 запросов к базе данных, возможно, они будут работать быстрее последовательно, чем параллельно, если все они поддерживаются одним и тем же устройством ввода-вывода, а запросы не находятся в кеше. Запуск их параллельно может привести к тому, что система будет иметь кучу запросов на чтение ввода-вывода почти одновременно, что может привести к тому, что ОС будет считывать маленькие бит каждого по очереди, что приведет к тому, что голова вашего диска начнет скатываться назад и вперед, тратя драгоценные миллисекунды.

Но это только догадка; невозможно реально определить, что вызывает замедление, не зная большего.

Хотя создание потоков «чрезвычайно дорого» по сравнению с добавлением двух чисел, обычно это не то, что вы легко переусердствуете. Если ваши операции чрезвычайно короткие (скажем, миллисекунда или меньше), использование пула потоков, а не новых потоков заметно сэкономит время. В общем, хотя, если ваши операции короткие, вы должны в любом случае пересмотреть гранулярность параллелизма; возможно, вам лучше расщепить вычисление на более крупные куски: например, имея довольно небольшое количество рабочих задач, которые обрабатывают целые партии меньших рабочих элементов одновременно, а не каждый элемент отдельно.

2

Есть так много проблем с этим, что он платит, чтобы понять, что происходит под обложками. Я бы очень рекомендовал книгу «Параллельное программирование в Windows» Джо Даффи и книгу «Java Concurrency in Practice». Последний говорит о архитектуре процессора на уровне, который вам нужно понять при написании многопоточного кода. Одной из проблем, которые вы собираетесь нанести, будет вред для вашего кода, является кеширование или, скорее, его отсутствие.

Как было указано, для планирования и выполнения потоков есть накладные расходы, но при совместном использовании данных по потокам вы можете обнаружить большие накладные расходы. Эти данные могут быть сброшены из кэша процессора в основную память, что может привести к серьезным замедлениям вашего кода.

Это своего рода низкоуровневый материал, который управляет средами, которые должны защитить нас, однако, при написании высокопараллельного кода, это именно та проблема, с которой вам приходится иметь дело.

Мой коллега записал скринкаст о проблеме с параметрами Parallel.For и Parallel.ForEach, которые могут помочь:

http://rocksolidknowledge.com/ScreenCasts.mvc/Watch?video=ParallelLoops.wmv