Подтвердить что ты не робот

Что такое PTHREAD_MUTEX_ADAPTIVE_NP

Где я могу найти документацию для "адаптивных" мьютексов pthread? В моей системе определен символ PTHREAD_MUTEX_ADAPTIVE_NP, но только документация, которую я могу найти в Интернете, ничего не говорит о том, что такое адаптивный мьютекс или когда он подходит для использования.

Итак... что это такое, и когда я должен его использовать?

Для справки, моя версия libc:

GNU C Library (Ubuntu EGLIBC 2.15-0ubuntu10.5) stable release version 2.15, by Roland McGrath et al.
Copyright (C) 2012 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.
There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE.
Compiled by GNU CC version 4.6.3.
Compiled on a Linux 3.2.50 system on 2013-09-30.
Available extensions:
    crypt add-on version 2.1 by Michael Glad and others
    GNU Libidn by Simon Josefsson
    Native POSIX Threads Library by Ulrich Drepper et al
    BIND-8.2.3-T5B
libc ABIs: UNIQUE IFUNC
For bug reporting instructions, please see:
<http://www.debian.org/Bugs/>.

и "uname -a" дает

Linux desktop 3.2.0-55-generic #85-Ubuntu SMP Wed Oct 2 12:29:27 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux
4b9b3361

Ответ 1

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

Ответ 2

PTHREAD_MUTEX_ADAPTIVE_NP - это то, что я придумал, работая в роли glibc-автора, чтобы сделать LinuxThreads более надежным и работать лучше. LinuxThreads был предшественником glibc-библиотеки NPTL, первоначально разработанной как автономная библиотека Ксавьером Лерой, который также известен как один из создателей OCaml.

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

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

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

Вопрос в том, как долго вы должны ждать? Если вы зависнете навсегда до тех пор, пока не будет получен замок, это может оказаться неоптимальным. Программы пользовательского пространства не очень хорошо написаны, как код ядра (кашель). У них могут быть длинные критические секции. Они также не могут отключить превенцию; иногда критические разделы взрываются из-за переключения контекста. (Потоки POSIX теперь предоставляют инструменты реального времени, чтобы справиться с этим: вы можете поместить потоки в приоритет в режиме реального времени и планирование FIFO и т.д., А также настроить сродство процессора.)

Я думаю, что мы экспериментировали с фиксированными подсчетами итераций, но тогда у меня была эта идея: почему мы должны догадываться, когда мы можем измерить. Почему мы не реализуем сглаженную оценку продолжительности блокировки, аналогично тому, что мы делаем для оценки тайм-аута повторной передачи TCP (RTO). Каждый раз, когда мы вращаемся по замку, мы должны измерить, сколько спинов он фактически взял, чтобы приобрести его. Более того, мы не должны вращаться навсегда: мы, возможно, должны откручивать не более чем вдвое больше текущего значения оценки. Когда мы проводим измерение, мы можем сгладить его экспоненциально всего лишь несколькими инструкциями: взять часть предыдущего значения и нового значения и добавить их вместе, что равнозначно добавлению части их разницы в обратную сторону к оценке: скажем, estimator += (new_val - estimator)/8 для комбинации от 1/8 до 7/8 между старым и новым значением.

Вы можете думать об этом как о сторожевом пса. Предположим, что оценщик сообщает вам, что в среднем блокировка занимает 80 спинов. Вы можете быть вполне уверены в том, что если вы выполнили 160 спинов, то что-то не так: владелец блокировки выполняет какой-то исключительно длинный случай или, возможно, попал на страницу или был вытеснен. В этот момент ожидающий поток сокращает свои потери и вызывает блокировку ядра.

Без измерения вы не можете сделать это точно: нет значения "один размер подходит всем". Скажем, фиксированный предел в 200 спинов был бы субоптимальным в программе, чьи критические разделы настолько коротки, что блокировку можно почти всегда извлекать после ожидания всего 10 спинов. Функция блокировки мьютекса будет гореть через 200 итераций каждый раз, когда будет аномальное время ожидания, вместо того, чтобы красиво отказаться от, скажем, 20 и сохранять циклы.

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

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

Ответ 3

Здесь упоминается символ:

http://elias.rhi.hi.is/libc/Mutexes.html

"LinuxThreads поддерживает только один атрибут mutex: тип mutex, который является PTHREAD_MUTEX_ADAPTIVE_NP для" быстрых "мьютексов, PTHREAD_MUTEX_RECURSIVE_NP для" рекурсивных "мьютексов, PTHREAD_MUTEX_TIMED_NP для" временных "мьютексов или PTHREAD_MUTEX_ERRORCHECK_NP для мьютексов" проверки ошибок ". Суффикс NP указывает, что это непереносимое расширение стандарта POSIX и не должно использоваться в переносных программах.

Тип мьютекса определяет, что произойдет, если поток попытается заблокировать мьютекс, который уже принадлежит pthread_mutex_lock. Если мьютекс имеет "быстрый" тип, pthread_mutex_lock просто приостанавливает вызывающий поток навсегда. Если мьютекс относится к типу "проверки ошибок", pthread_mutex_lock немедленно возвращается с кодом ошибки EDEADLK. Если мьютекс имеет "рекурсивный" тип, вызов pthread_mutex_lock немедленно возвращается с кодом возврата успеха. Число задержек, в которых поток, имеющий мьютекс, заблокирован, записывается в мьютекс. Собственный поток должен вызывать pthread_mutex_unlock столько же раз, пока мьютекс не вернется в незаблокированное состояние.

Тип мьютекса по умолчанию "timed", то есть PTHREAD_MUTEX_TIMED_NP. "

EDIT: обновлено с информацией, полученной jthill (спасибо!)

Немного больше информации о флагах mutex и PTHREAD_MUTEX_ADAPTIVE_NP можно найти здесь:

"PTHRED_MUTEX_ADAPTIVE_NP - это новый мьютекс, предназначенный для высоких пропускной способности при жертве справедливости и даже циклов ЦП. Эта mutex не передает права собственности на ожидающий поток, а скорее позволяет соревноваться. Кроме того, над ядром SMP выполняется операция блокировки использует поворот, чтобы повторить блокировку, чтобы избежать затрат на немедленное descheduling ".

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

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