Многоячеечное программирование: жесткие детали

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

Ответы

Ответ 1

Какая полезная единица работы для распараллеливания и как ее найти/организовать?

Все эти примитивы parallelism не полезны, если вы разветвляете часть работы, которая меньше, чем накладные расходы; фактически, что покупает вам хорошее замедление, а не то, что вы ожидаете.

Таким образом, одна из больших проблем - найти единицы работы, которые, очевидно, дороже, чем примитивы parallelism. Ключевой проблемой здесь является то, что никто не знает, что стоит затратить на выполнение, включая сами примитивы parallelism. Четкая калибровка этих затрат будет очень полезной. (В стороне мы разработали, внедрили и ежедневно использовали параллельное программирование langauge, PARLANSE, целью которого было минимизировать стоимость parallelism, позволяя компилятору генерировать и оптимизировать их, с целью сделать более мелкие кусочки работы "более параллельными" ).

Можно было бы также рассмотреть вопрос о большой нотации и ее приложениях. Мы все надеемся, что примитивы parallelism стоят O (1). Если это так, то если вы найдете работу со стоимостью O (x) > O (1), то эта работа является хорошим кандидатом для распараллеливания. Если ваша предлагаемая работа также является O (1), то является ли она эффективной или нет, зависит от постоянных факторов, и мы вернемся к калибровке, как указано выше.

Там проблема сбора работы на достаточно большие единицы, если ни одна из частей не является достаточно большой. Движение кода, замена алгоритма,... являются полезными идеями для достижения этого эффекта.

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

Ответ 2

Я думаю, что некоторые из них зависят от того, насколько основной или продвинутой книга/аудитория. Когда вы переходите от однопоточного к многопоточному программированию в первый раз, вы обычно отваливаетесь от огромного утеса (и многие из них никогда не восстанавливаются, см., Например, все запутанные вопросы о Control.Invoke).

В любом случае, чтобы добавить некоторые мысли, которые касаются самого программирования, и больше о других связанных задачах в программном процессе:

  • Измерение: определение того, какую метрику вы пытаетесь улучшить, правильно ее измерить (так легко случайно измерить неправильную вещь), используя правильные инструменты, дифференцируя сигнал и шум, интерпретируя результаты и понимая, почему они такие, как они.

  • Тестирование: как писать тесты, которые допускают неважные детерминизм/перемежения, но все же вызывают правильное поведение программы.

  • Отладка: инструменты, стратегии, когда "трудно отлаживать" подразумевает обратную связь для улучшения вашего кода/дизайна и улучшения состояния перераспределения раздела и т.д.

  • Физическое или логическое сходство потоков: понимание потока GUI, понимание того, как, например, F # MailboxProcessor/agent может инкапсулировать изменчивое состояние и работать в нескольких потоках, но всегда с одним логическим потоком (один программный счетчик).

  • Шаблоны (и когда они применяются): fork-join, map-reduce, производитель-потребитель,...

Я ожидаю, что будет большая аудитория, например. "help, у меня однопоточное приложение с 12% -ным использованием процессора, и я хочу узнать достаточно, чтобы сделать его быстрее на 4 раза без большой работы" и меньшую аудиторию, например. "мое приложение масштабируется сублинейно, так как мы добавляем ядра, потому что здесь, кажется, существует спор, есть ли более эффективный подход к использованию?", и поэтому некоторые проблемы могут обслуживать каждую из этих аудиторий.

Ответ 3

Поскольку вы пишете целую книгу для многоядерного программирования в .Net.

Я думаю, что вы также можете выйти за рамки многоядерных.

Например, вы можете использовать главу, посвященную параллельным вычислениям в распределенной системе в .Net. Маловероятно, что в .Net еще нет зрелых фреймворков. DryadLinq является самым близким. (С другой стороны, Hadoop и его друзья на платформе Java действительно хороши.)

Вы также можете использовать раздел, демонстрирующий некоторые вычислительные средства графического процессора.

Ответ 4

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

Ответ 5

Чтобы понять: детали памяти низкого уровня, такие как разность между семантикой памяти получения и выпуска.

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

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

Ответ 6

Не так много теоретических деталей, но больше деталей практической реализации, которые вызывают людей.

Какая сделка с неизменяемыми структурами данных?

Все время люди пытаются обновить структуру данных из нескольких потоков, находят ее слишком тяжело, и кто-то звонит в "использовать неизменные структуры данных!", и поэтому наш постоянный кодер пишет это:

ImmutableSet set;

ThreadLoop1()
    foreach(Customer c in dataStore1)
        set = set.Add(ProcessCustomer(c));

ThreadLoop2()
    foreach(Customer c in dataStore2)
        set = set.Add(ProcessCustomer(c));

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

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

Как распределить примерно одинаковое количество работы между потоками?

Правильно сделать этот шаг сложно. Иногда вы разбиваете один процесс на 10 000 шагов, которые могут выполняться параллельно, но не все шаги занимают одинаковое количество времени. Если вы разделите работу на 4 потока, а первые 3 потока закончите за 1 секунду, а последний поток займет 60 секунд, ваша многопоточная программа не намного лучше, чем однопоточная версия, правильно?

Итак, как вы разделяете проблемы с примерно равным объемом работы между всеми потоками? Здесь должно быть уместно много хорошей эвристики при решении проблем с упаковкой бункеров.

Сколько потоков?

Если ваша проблема хорошо распараллеливается, добавление большего количества потоков должно ускорить работу, верно? Ну не очень, много вещей, чтобы рассмотреть здесь:

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

Итак, как вы выделяете достаточно потоков для минимизации времени выполнения?

И если есть много других приложений, которые занимаются потоками и конкурируют за ресурсы, как вы обнаруживаете изменения в производительности и автоматически настраиваете свою программу?

Ответ 7

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

Обычно я нахожу, что отладка тоже медведь.