Привязка к процессору

Привязка к процессору (англ. processor affinity), или закрепление процессора, или привязка к кэшу, — технология, которая обеспечивает закрепление и открепление процесса или потока к конкретному ядру центрального процессора, центральному процессору или набору процессоров, так что процесс или поток будут выполняться только на указанном ядре, процессоре или процессорах, а не на любом процессоре многопроцессорной системы. Привязку процессора можно рассматривать как модификацию алгоритма планирования центральной очереди задач в многопроцессорной операционной системе. Каждому элементу в очереди задач сопоставлен тег, задающие «родственные» ему процессоры.

При выделении ресурсов каждая задача предпочтительным образом распределяется для выполнения на одном из «родственных» процессоров. Технология привязки к процессору использует тот факт, что данные и настройки процесса, который ранее был запущен на данном процессоре, могут быть оказаться более быстродоступными для этого процессора, чем для какого-то ещё. Это может происходить, например, из-за кэширования данных процесса в кэш-памяти процессора, а так же в некоторых других ситуациях. Планирование выполнения такого процесса на одном и том же процессоре повышает его производительность за счет уменьшения снижающих производительность событий, таких как потери в кеше.

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

Практическим примером сродства процессора является выполнение нескольких экземпляров немногопоточного приложения, такого как некоторое программное обеспечение для рендеринга графики.

Реализация алгоритма планирования задач, обеспечивающего возможность привязки к процессору, реализуется с учётом особенностей конкретных процессора и построения многопроцессорной системы, управляя которой будет работать такой алгоритм. Некоторые реализации при определенных обстоятельствах позволят перевести задачу на другой процессор, преодолевая привязку. Это делается в тех случаях, когда, с точки зрения планировщика, такое переключение приведет к повышению эффективности выполнения задач. Например, когда двум задачам, интенсивно использующих процессор (A и B), установлена привязка к одному и тому же процессору, а другой процессор не используется, многие планировщики переключат задачу B на второй процессор, чтобы максимально использовать доступные системе процессорные мощности. Привязка задачи B к новому процессору в такой момент будет выставлена самим планировщиком.

Особенности реализации

Привязка к процессору может эффективно уменьшить проблемы с попаданием данных в системный и/или процессорный кэш. Но она не обеспечивает решение проблем с балансировкой нагрузки[1]. Привязка к процессору является более сложной задачей в системах с неоднородной архитектурой, требуя от планировщика более изощрённой логики работы, чем в полностью однородных системах. Например, система с двумя двухъядерными CPU, каждый из которых поддерживает технологию Hyper-Threading представляет собой проблему для алгоритма планировщика, предполагающего учёт привязки к процессору. Если же система обладает ещё большим числом процессоров, и, например, сама по себе не является полностью симметричной, то сложность проблемы эффективного планирования выполнения задач увеличивается ещё сильнее.

Для приведённого выше примера с двумя двухъядерными процессорами с hyper-threading, планировщик должен реализовывать двухуровневую систему привязки. С точки зрения эффективности работы с кэшом, работа в рамках одного ядра на разных нитях является равнозначной и планировщик имеет право свободно перемещать задачу с нити на нить. Уровень «близости» различных ядер внутри одного процессора ниже, так как они частично разделяют общий процессорный кэш, уровень «близости» различных процессоров еще ниже. Поскольку другие ресурсы также используются совместно, одна только привязка к процессору не может использоваться в качестве основы для диспетчеризации задач. Например, если процесс недавно выполнялся на одном виртуальном hyper-threading CPU в некотором ядре, и этот виртуальный CPU в настоящее время занят, но второй виртуальный CPU того же ядра не занят, привязка к процессору, исходя из эффективности использования кэш-памяти подразумевает, что процесс должен быть переведён на второй (не работающий) виртуальный процессор того же ядра. Однако два виртуальных CPU конкурируют практически за все вычислительные ресурсы, кэш-память и ресурсы памяти. В этой ситуации, как правило, было бы более эффективно назначать процесс на другое ядро или CPU, если среди них есть незагруженные. Это может повлечь за собой разовый провал производительности за счёт того, что перемещённый процесс должен будет снова пополнять кэш своими данными. Но общая производительность может оказаться выше, поскольку двум процессам не придется конкурировать за ресурсы внутри одного CPU.

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

Реализация в конкретных операционных системах

В Linux привязка процесса к процессору может быть выяснена или установлена при помощи утилиты taskset[2]. На программном уровне те же действия могут быть выполнены при помощи системных вызовов sched_getaffinity и sched_setaffinity[3]. Привязку нити можно установить или изменить с помощью одной из функций библиотеки: pthread_setaffinity_np[4] или pthread_attr_setaffinity_np[5].

В системах SGI процесс с набором процессоров можно было связать утилитой dplace[6].

В DragonFly BSD 1.9 (2007) и более поздних версиях для управления привязкой к процессору может использоваться системный вызов usched_set[7][8]. В NetBSD 5.0, FreeBSD 7.2, DragonFly BSD 4.7 и более поздних версиях можно использовать системные вызовы pthread_setaffinity_np и pthread_getaffinity_np[9]. В NetBSD утилита[10] psrset устанавливает привязку нити к определенному набору CPU. Во FreeBSD утилита cpuset[11] используется для создания наборов процессоров и назначения процессов этим наборам. В DragonFly BSD 3.1 (2012) и более поздних версиях утилита usched может использоваться для назначения процессов определенному набору процессоров[12].

В Windows NT и следующих версиях, привязка нитей и процессов можно устанавливаться отдельно с помощью вызовов API SetThreadAffinityMask[13] и SetProcessAffinityMask[14] или через интерфейс диспетчера задач (только для процессов).

macOS предоставляет API-интерфейс привязки[15], который передаёт ядру ОС подсказки о том, как планировать потоки в соответствии с наборами привязок.

В Solaris можно контролировать привязку процессов и легких процессов к процессору с помощью утилиты pbind[16]. Так же предоставляется системный вызов processor_bind[17]. Доступны также интерфейсные вызовы более высокого уровня, а именно — pset_bind[18] или lgrp_affinity_get[19], использующие концепции набора процессоров и групп локальности соответственно.

В AIX можно управлять привязками процессов с помощью утилиты bindprocessor[20][21] и системного вызова bindprocessor[20][22].

В z/OS реализован, возможно, наиболее сложный из находящихся в современной эксплуатации планировщик задач. Он обеспечивает динамически изменяемое перераспределение ресурсов оборудования между процессами, в том числе и на основе привязки процессов к отдельным ядрам процессоров, процессорам и их группам[23]

Поддержка в языках программирования

Стандартная библиотека языка программирования Julia, ориентированного на параллельные вычисления, включает экспериментальную поддержку привязки процессов к процессору[24].

Примечания

  1. «White Paper — Processor Affinity» — на tmurgent.com.
  2. taskset(1)  страница справки man для разработчика Linux — пользовательские команды  (англ.)
  3. sched_setaffinity(2)  страница справки man для разработчика Linux — системные вызовы  (англ.)
  4. pthread_setaffinity_np(3)  страница справки man для разработчика Linux — библиотечные функции  (англ.)
  5. pthread_attr_setaffinity_np(3)  страница справки man для разработчика Linux — библиотечные функции  (англ.)
  6. dplace.1 Архивировано 1 июля 2007 года. — From sgi.com. Accessed 2007-07-06.
  7. usched_set(2) — setting up a proc's usched. DragonFly System Calls Manual. DragonFly BSD. Дата обращения: 28 июля 2019.
  8. kern/kern_usched.c § sys_usched_set. BSD Cross Reference. DragonFly BSD. Дата обращения: 28 июля 2019.
  9. pthread_setaffinity_np(3) — NetBSD, FreeBSD and DragonFly BSD Library Functions Manual
  10. psrset(8)  страница справки man системного администратора NetBSD  (англ.)
  11. cpuset(1)  страница справки man по пользовательским командам FreeBSD  (англ.)
  12. usched(8) — run a program with a specified userland scheduler and cpumask. DragonFly System Manager's Manual. DragonFly BSD. Дата обращения: 28 июля 2019.
  13. SetThreadAffinityMask — MSDN Library
  14. SetProcessAffinityMask — MSDN Library
  15. Thread Affinity API Release Notes. Developer.apple.com.
  16. pbind(1M) — Solaris man page
  17. processor_bind(2) — Solaris man page
  18. pset_bind(2) — Oracle Solaris 11.1 Information Library — man pages section 2
  19. lgrp_affinity_get(3LGRP) — Memory and Thread Placement Optimization Developer’s Guide
  20. Umesh Prabhakar Gaikwad; Kailas S. Zadbuke. Processor affinity on AIX (November 16, 2006).
  21. bindprocessor Command. IBM.
  22. bindprocessor Subroutine. IBM.
  23. IBM zEnterprise 196 Technical Guide. Bill White, Erik Bakker, Parwez Hamid, Octavian Lascu, Fernando Nogal, Frank Packeiser, Vicente Ranieri Jr., Karl-Erik Stenfors, Esra Ufacik, Chen Zhu, IBM Redbooks. October 2011
  24. Обсуждение разработчиков
This article is issued from Wikipedia. The text is licensed under Creative Commons - Attribution - Sharealike. Additional terms may apply for the media files.