Многопоточное программирование POSIX Threads
Выбери формат для чтения
Загружаем конспект в формате pdf
Это займет всего пару минут! А пока ты можешь прочитать работу в формате Word 👇
Лекция 4
Многопоточное программирование
POSIX Threads (1)
Курносов Михаил Георгиевич
E-mail: mkurnosov@gmail.com
WWW: www.mkurnosov.net
Курс «Параллельные вычислительные технологии»
Сибирский государственный университет телекоммуникаций и информатики (г. Новосибирск)
Весенний семестр, 2018
CC-BY
Средства многопоточного программирования
Прикладные библиотеки
▪
▪
▪
▪
▪
Языки программирования
▪ OpenMP
(C/C++/Fortran)
▪ Intel Cilk Plus
▪ С++11 Threads
▪ C11 Threads
Intel Threading Building Blocks (TBB)
Microsoft Concurrency Runtime
Apple Grand Central Dispatch
Boost Threads
Qthread, MassiveThreads
▪
▪
▪
▪
OpenMP
C# Threads
Java Threads
Erlang Threads
Haskell Threads
Системные библиотеки (System libraries)
Уровень
пользователя
(User space)
POSIX Threads
Thread
Thread
Уровень ядра
(Kernel space)
Win32 API/.NET Threads
Thread
Thread
Thread
Apple OS X Cocoa, Pthreads
Thread
Thread
…
Thread
Системные вызовы (System calls)
Kernel
thread
Kernel
thread
Kernel
thread
Kernel
thread
Kernel
thread
Kernel
thread
Kernel
thread
…
Process scheduler
Операционная система
GNU/Linux, Microsoft Windows, Apple OS X, IBM AIX, Oracle Solaris, …
Hardware (Multi-core processor, SMP/NUMA)
pthreads
(glibc)
Kernel
thread
clone()
(Linux syscall)
Процессы и потоки
Стек потока 0
Стек потока 1
…
Стек потока N - 1
Куча (heap)
(динамически выделяемая память: malloc/free)
Область неинициализированных данных (BSS)
(глобальные неинициализированные переменные)
Область инициализированных данных (Data)
(глобальные инициализированные переменные)
Поток 0
int fun()
{
// …
}
Поток 1
int fun()
{
// …
}
Поток N-1
…
int fun()
{
// …
}
▪ Программа (program) – исполняемый файл в
определенном формате
o GNU/Linux – ELF
o Microsoft Windows – PE
o Apple maxOS – Mach-O
▪ Процесс (process) – экземпляр запущенной
программы
▪ Виртуальное адресное пространство
(address space)
▪ Главный поток выполнения (thread)
▪ Поток выполнения (thread) – код и локальная
область памяти для хранения данных:
стек и TLS – thread local storage
Программы и процессы
#include
const int globalConst = 100;
int globalVar = 10;
int globalVarNI;
int main()
{
int stackVar = 10;
static int staticVar = 100;
printf("Hello, World!\n");
return 0;
}
$ gcc –o prog ./prog.c
# Символы исполняемого файла
$ objdump --syms ./prog
./prog:
file format elf64-x86-64
SYMBOL TABLE:
00000000004005b0
0000000000601024
0000000000601028
00000000004004f6
0000000000601030
g
g
l
g
g
O
O
O
F
O
.rodata
.data
.data
.text
.bss
0000000000000004
0000000000000004
0000000000000004
0000000000000020
0000000000000004
globalConst
globalVar
staticVar.2214
main
globalVarNI
▪ Исполняемый файл содержит секции, которые сформированы
компилятором (gcc) и компоновщиком (ld)
o
o
o
o
.rodata – глобальные константы
.data – инициализированные глобальные переменные
.bss – неинициализированные глобальные переменные
.text – код программы
▪ При запуске программы секции загружаются в память процесса
Программы и процессы
#include
const int globalConst = 100;
int globalVar = 10;
int globalVarNI;
int main()
{
int stackVar = 10;
static int staticVar = 100;
int stackArray[10000000];
return 0;
}
$ ulimit -a
core file size
(blocks, -c) unlimited
data seg size
(kbytes, -d) unlimited
scheduling priority
(-e) 0
file size
(blocks, -f) unlimited
pending signals
(-i) 61834
max locked memory
(kbytes, -l) 64
max memory size
(kbytes, -m) unlimited
open files
(-n) 1024
pipe size
(512 bytes, -p) 8
POSIX message queues
(bytes, -q) 819200
real-time priority
(-r) 0
stack size
(kbytes, -s) 8192
cpu time
(seconds, -t) unlimited
max user processes
(-u) 61834
virtual memory
(kbytes, -v) unlimited
file locks
(-x) unlimited
$ ./prog
Segmentation fault (core dumped)
▪ Размер стека главного потока равен 8 KiB
(8 * 1024 * 1024 / 4 элементов типа int, 4 * 1024 * 1024)
▪ В программе создан автоматический массив из 10 000 000 элементов
Процессы и потоки
Стек потока 0
// Uninitialized data (BSS)
int sum[100]; // BSS
Стек потока 1
…
// Initialized data (Data)
float grid[100][100] = {1.0};
Стек потока N - 1
Куча (heap)
(динамически выделяемая память: malloc/free)
Область неинициализированных данных (BSS)
(глобальные неинициализированные переменные)
int main()
{
// Local variable (stack)
double s = 0.0;
// Allocate from the heap
float *x = malloc(1000);
// ...
free(x);
Область инициализированных данных (Data)
(глобальные инициализированные переменные)
Поток 0
int fun()
{
// …
}
Поток 1
int fun()
{
// …
}
Поток N-1
…
int fun()
{
// …
}
}
Стандарт POSIX Threads
▪ POSIX.1c: Threads extensions (IEEE Std 1003.1c-1995) – определяет API для управления потоками
Thread Creation, Control, and Cleanup
Thread Scheduling
Thread Synchronization
Signal Handling
▪ The Single UNIX Specification // http://www.unix.org/version4/
▪ Реализации POSIX Threads
FreeBSD, NetBSD, OpenBSD, GNU/Linux, Apple OS X, Solaris
Windows Services for UNIX (POSIX subsystem), pthreads-w32, mingw-w64 pthreads
▪ man pthread
▪ man nptl
Модель fork-join управления потоками
Thread 0
Fork Fork
Join Join
Thread 1
▪ Join – ожидание
одним потоком
завершения другого
(объединение
потоков управления)
Fork
Thread 2
Fork
Thread 3
Thread 4 (detached)
Время
▪ Fork –
создание нового потока
Модель fork-join управления потоками
Email Client
(GUI thread + data process thread)
Thread 0
Drawing GUI
Fork
Terminate
Thread 1
Loading emails
in background
Time
Модель fork-join управления потоками
Multithreaded HTTP-server
Master thread + 1 thread per request
Thread 0
Accepting requests
Fork
Worker thread
Loading HTML-document
and sending response
Time
Модель fork-join управления потоками
Multithreaded HTTP-server
Master thread + thread pool
Thread 0
Queue of requests
Terminate
Fork
Worker thread
Processing requests
from the queue
Time
Модель fork-join управления потоками
Recursive Parallelism – Divide & Conquer
(Parallel QuickSort, Reductions, For loops)
fib(4)
fib(3)
fib(2)
fib(1)
fib(0)
fib(2)
fib(1)
fib(1)
fib(0)
function fib(int n)
if n < 2 then
return n
x = fork threadX fib(n - 1)
y = fork threadY fib(n - 2)
join threadX
join threadY
return x + y
end function
Обзор POSIX Threads
#include
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
int pthread_join(pthread_t thread, void **retval);
int pthread_attr_init(pthread_attr_t *attr);
int pthread_cancel(pthread_t thread);
pthread_t pthread_self(void);
int pthread_equal(pthread_t t1, pthread_t t2);
Hello, World of POSIX Threads!
#include
#include
void *thread_func(void *arg)
{
// Код дочернего потока
pthread_t tid = pthread_self();
printf("Hello from slave thread %u\n", (unsigned int)tid);
sleep(5);
return NULL;
}
$ gcc -Wall -pthread ./prog.c –oprog
$ ./prog
Hello from master thread 1971812096
Hello from slave thread 1963546368
Поток завершается при:
▪ Вызове в нем pthread_exit()
▪ Выходе из функции потока
int main()
{
▪ Отмене вызовом pthread_cancel()
pthread_t tid;
int rc = pthread_create(&tid, NULL, thread_func, NULL);
▪ При вызове exit() все потоки процесса
if (rc != 0) {
принудительно завершаются
fprintf(stderr, "Can't create thread\n");
(например, при выходе из main())
exit(EXIT_FAILURE);
}
printf("Hello from master thread %u\n", (unsigned int)pthread_self());
// Мастер-поток ожидает завершение дочернего потока
pthread_join(tid, NULL);
return 0;
}
Hello, World of POSIX Threads!
#include
#include
$ gcc -Wall -pthread ./prog.c –oprog
void *thread_func(void *arg)
$ ./prog
{
Hello from master thread 4256687872
// Код дочернего потока
Hello from slave thread 4248422144
pthread_t tid = pthread_self();
printf("Hello from slave thread %u\n", (unsigned int)tid);
sleep(5);
return NULL;
$ ps -L -o comm,pid,lwp,nlwp,psr,pmem,pcpu -C prog
}
COMMAND
prog
prog
PID
LWP NLWP PSR %MEM %CPU
18357 18357
2
1 0.0 0.0
18357 18358
2
0 0.0 0.0
int main()
{
pthread_t tid;
int rc = pthread_create(&tid, NULL, thread_func, NULL);
if (rc != 0) {
fprintf(stderr, "Can't create thread\n");
exit(EXIT_FAILURE);
}
printf("Hello from master thread %u\n", (unsigned int)pthread_self());
// Мастер-поток ожидает завершение дочернего потока
pthread_join(tid, NULL);
return 0;
}
LWP – thread id
NLWP – число потоков процесса
PSR – номер процессора
Запуск N потоков
int main(int argc, char **argv)
$ ./prog
{
Launching 4 threads
int nthreads = argc > 1 ? atoi(argv[1]) : 4;
pthread_t *tids = malloc(sizeof(*tids) * nthreads);
Hello from slave thread 2625984256
if (tids == NULL) {
Hello from slave thread 2617591552
fprintf(stderr, "No enough memory\n");
Hello from master thread 2634249984
exit(EXIT_FAILURE);
Hello from slave thread 2609198848
}
Hello from slave thread 2600806144
printf("Launching %d threads\n", nthreads);
for (int i = 0; i < nthreads; i++) {
if (pthread_create(&tids[i], NULL, thread_func, NULL) != 0) {
fprintf(stderr, "Can't create thread\n");
exit(EXIT_FAILURE);
}
}
// Now we have nthreads + 1 master thread
printf("Hello from master thread %u\n",
$ ps -L -o comm,pid,lwp,nlwp,psr,pmem,pcpu -C prog
(unsigned int)pthread_self());
for (int i = 0; i < nthreads; i++)
pthread_join(tids[i], NULL);
free(tids);
return 0;
}
COMMAND
prog
prog
prog
prog
prog
PID
18697
18697
18697
18697
18697
LWP NLWP PSR %MEM %CPU
18697
5
1 0.0 0.0
18698
5
2 0.0 0.0
18699
5
2 0.0 0.0
18700
5
0 0.0 0.0
18701
5
0 0.0 0.0
Формула средних прямоугольников (midpoint rule)
для вычисления определенного интеграла
double func(double x)
{
return exp(-x * x);
}
f(x)
Второй порядок точности
int main(int argc, char **argv)
{
const double a = -4.0;
const double b = 4.0;
const int n = 100;
a
x0
double h
double s
for (int
s +=
s *= h;
x
x1
x2
…
xn-1
b
xn
= (b - a) / n;
= 0.0;
i = 0; i < n; i++)
func(a + h * (i + 0.5));
printf("Result Pi: %.12f\n", s * s);
return 0;
}
Параллельный алгоритм интегрирования методом
средних прямоугольников (midpoint rule)
▪ Каждый поток вычисляет частичную сумму
(часть площади по кривой)
f(x)
▪ После чего, результаты суммируются
(свойство аддитивности интеграла)
double func(double x)
{
return exp(-x * x);
}
int main(int argc, char **argv)
{
const double a = -4.0;
const double b = 4.0;
const int n = 100;
a
x0
double h
double s
for (int
s +=
s *= h;
x
x1
x2
…
xn-1
b
xn
= (b - a) / n;
= 0.0;
i = 0; i < n; i++)
func(a + h * (i + 0.5));
printf("Result Pi: %.12f\n", s * s);
return 0;
}
Параллельный алгоритм интегрирования методом
средних прямоугольников (midpoint rule)
1. Итерации цикла for распределяются между потоками
2. Каждый поток вычисляет часть суммы (площади)
3. Суммирование результатов потоков (во всех или одном)
double func(double x)
{
return exp(-x * x);
}
int main(int argc, char **argv)
{
const double a = -4.0;
const double b = 4.0;
const int n = 100;
Варианты распределения итераций (точек)
между потоками:
1) Разбиение на p смежных непрерывных частей
T0
T0
T0
T0
T1
TID 0
T1
T1
T1
T2
T2
T2
double h
double s
for (int
s +=
s *= h;
T2
TID 2
TID 1
= (b - a) / n;
= 0.0;
i = 0; i < n; i++)
func(a + h * (i + 0.5));
2) Циклическое распределение итераций по потокам
T0
T1
T2
T0
T1
T2
T0
TID 0, TID 1, TID 2, TID 0, TID 1, TID 2, ...
T1
T2
T0
T1
printf("Result Pi: %.12f\n", s * s);
return 0;
T2
}
Параллельный алгоритм интегрирования методом
средних прямоугольников (midpoint rule)
double func(double x)
{
return exp(-x * x);
}
Каждому потоку необходимо передать:
▪ порядковый номер потока
▪ число потоков
▪ h, n, a
int main(int argc, char **argv)
{
const double a = -4.0;
const double b = 4.0;
const int n = 100;
double h
double s
for (int
s +=
s *= h;
lb
T0
T0
T0
TID 0
T0
T1
printf("Result Pi: %.12f\n", s * s);
return 0;
ub
T1
T1
TID 1
T1
= (b - a) / n;
= 0.0;
i = 0; i < n; i++)
func(a + h * (i + 0.5));
T2
T2
T2
TID 2
T2
}
Интегрирование методом средних прямоугольников (midpoint rule)
struct thread_data {
pthread_t tid;
int threadno;
int nthreads;
};
// Порядковый номер потока 0, 1, 2, …
// Количество потоков
int main(int argc, char **argv)
{
int nthreads = (argc > 1) ? atoi(argv[1]) : 2;
printf("Numerical integration (%d threads): [%f, %f], n = %d\n", nthreads, a, b, n);
struct thread_data *tdata = malloc(sizeof(*tdata) * nthreads);
if (tdata == NULL) {
fprintf(stderr, "No enough memory\n");
exit(EXIT_FAILURE);
}
for (int i = 1; i < nthreads; i++) {
// Запуск nthreads – 1 потоков!
tdata[i].threadno = i;
tdata[i].nthreads = nthreads;
if (pthread_create(&tdata[i].tid, NULL, integrate, &tdata[i]) != 0) {
fprintf(stderr, "Can't create thread\n");
exit(EXIT_FAILURE);
}
}
tdata[0].threadno = 0;
tdata[0].nthreads = nthreads;
integrate(&tdata[0]);
// Мастер-поток участвует в вычислениях, всего nthreads потоков
Интегрирование методом средних прямоугольников (midpoint rule)
// продолжение main()
for (int i = 1; i < nthreads; i++)
pthread_join(tdata[i].tid, NULL);
free(tdata);
printf("Result Pi: %.12f\n", s * s);
return 0;
}
Интегрирование методом средних прямоугольников (midpoint rule)
const double a = -4.0;
const double b = 4.0;
const int n = 10000000;
// Глобальная сумма
double s = 0.0;
double func(double x) {
return exp(-x * x);
}
void *integrate(void *arg)
{
struct thread_data *p = (struct thread_data *)arg;
double h = (b - a) / n;
int points_per_proc = n / p->nthreads;
int lb = p->threadno * points_per_proc;
int ub = (p->threadno == p->nthreads - 1) ? (n - 1) : (lb + points_per_proc - 1);
for (int i = lb; i < ub; i++)
s += func(a + h * (i + 0.5));
s *= h; // Обновление глобальной суммы
return NULL;
}
// Вся суть распараллеливания – цикл распределен между потоками
lb
T0
T0
T0
TID 0
T0
T1
ub
T1
T1
TID 1
T1
T2
T2
T2
TID 2
T2
Интегрирование методом средних прямоугольников (midpoint rule)
struct thread_data {
pthread_t tid;
int threadno;
int nthreads;
};
// Порядковый номер потока 0, 1, 2, …
// Количество потоков
int main(int argc, char **argv)
{
Ошибка – при каждом запуске результат другой!
int nthreads = (argc > 1) ? atoi(argv[1]) : 2;
printf("Numerical integration (%d threads): [%f, %f], n = %d\n", nthreads, a, b, n);
./integrate
Numerical integration
(2 threads): [-4.000000, 4.000000], n = 10000000
struct thread_data *tdata = malloc(sizeof(*tdata)
* nthreads);
if (tdata == NULL) {
Result Pi: 0.638820620709
fprintf(stderr, "No enough memory\n");
exit(EXIT_FAILURE);
./integrate
}
Numerical integration (2 threads): [-4.000000,
for (int i = 1; i < nthreads; i++) {
// Запуск nthreads – 1 потоков!
Result
Pi:
0.000000000001
tdata[i].threadno = i;
tdata[i].nthreads = nthreads;
./integrate
if (pthread_create(&tdata[i].tid,
NULL, integrate, &tdata[i]) != 0) {
fprintf(stderr, "Can'tNumerical
create thread\n");
integration (2 threads): [-4.000000,
exit(EXIT_FAILURE); Result Pi: 1.724699625421
}
}
./integrate
tdata[0].threadno = 0;
tdata[0].nthreads = nthreads; Numerical integration (2 threads): [-4.000000,
Result Pi: //
0.701209910269
integrate(&tdata[0]);
Мастер-поток участвует в вычислениях,
4.000000], n = 10000000
4.000000], n = 10000000
4.000000], n = 10000000
всего nthreads потоков
Интегрирование методом средних прямоугольников (midpoint rule)
Состояние гонки данных (data race)
const double a = -4.0;
const double b = 4.0;
const int n = 10000000;
Несколько потоков осуществляют
конкурентный доступ к разделяемой
переменной s – одновременно читают
ее и записывают
// Глобальная сумма
double s = 0.0;
double func(double x) {
return exp(-x * x);
}
// s += X
Load s -> %reg0
Load X -> %reg1
Add %reg0 %reg1 -> %reg0
Store reg0 -> s
void *integrate(void *arg)
{
struct thread_data *p = (struct thread_data *)arg;
double h = (b - a) / n;
int points_per_proc = n / p->nthreads;
int lb = p->threadno * points_per_proc;
int ub = (p->threadno == p->nthreads - 1) ? (n - 1) : (lb + points_per_proc - 1);
for (int i = lb; i < ub; i++)
s += func(a + h * (i + 0.5));
s *= h; // Обновление глобальной суммы
return NULL;
}
// Вся суть распараллеливания – цикл распределен между потоками
lb
T0
T0
T0
TID 0
T0
T1
ub
T1
T1
TID 1
T1
T2
T2
T2
TID 2
T2
Состояние гонки данных (race condition, data race)
Два потока одновременно увеличивают значение переменной x на 1
(начальное значение x = 0)
Thread 0
x = x + 1;
Thread 1
x = x + 1;
Ожидаемый (идеальный) порядок выполнения потоков: первый поток увеличил x, затем второй
Time
Thread 0
Thread 1
x
Значение x = 0 загружается
в регистр R процессора
1
Значение 0 в регистре R
увеличивается на 1
2
Значение 1 из регистра R
записывается в x
1
3
Значение x = 1 загружается
в регистр R процессора
1
4
Значение 1 в регистре R
увеличивается на 1
1
5
Значение 2 из регистра R
записывается в x
2
Ошибки нет
Состояние гонки данных (race condition, data race)
Два потока одновременно увеличивают значение переменной x на 1
(начальное значение x = 0)
Thread 0
x = x + 1;
Thread 1
x = x + 1;
Реальный порядок выполнения потоков (недетерминированный)
(потоки могут выполняться в любой последовательности, приостанавливаться
и запускаться)
Time
Thread 0
Значение x = 0 загружается
в регистр R процессора
1
Значение 0 в регистре R
увеличивается на 1
Значение x = 0 загружается
в регистр R процессора
2
Значение 1 из регистра R
записывается в x
Значение 1 в регистре R
увеличивается на 1
1
Значение 1 из регистра R
записывается в x
1
3
Thread 1
x
Ошибка - data race
(ожидали 2)
Состояние гонки данных (data race)
▪ Состояние гонки (race condition, data race) – это состояние программы,
в которой несколько потоков одновременно конкурируют за доступ к общей
структуре данных (для чтения/записи)
▪ Порядок выполнения потоков заранее не известен – носит случайный характер
▪ Планировщик ОС динамически распределяет процессорное время учитывая
текущую загруженность процессорных ядер, нагрузку (потоки, процессы) создают
пользователи, поведение которых носит случайных характер
▪ Состояние гонки данных (race condition, data race) трудно обнаруживается
в программах и воспроизводится в тестах (Гейзенбаг – heisenbug)
Состояние гонки данных (data race)
Динамические анализаторы кода
▪ Valgrind Helgrind, DRD
▪ ThreadSanitizer – a data race detector for C/C++ and Go (gcc 4.8, clang)
▪ Intel Thread Checker
▪ Oracle Studio Thread Analyzer
▪ Java ThreadSanitizer
▪ Java Chord
Статические анализаторы кода
▪ PVS-Studio (Viva64)
Понятие критической секции (critical section)
▪ Критическая секция (critical section) — это участок исполняемого кода,
который в любой момент времени выполняется только одним потоком
Critical
section
Thread 0
Wait
Thread 1
Thread 2
CS
CS
Wait
Time
Мьютексы (mutex)
▪ Мьютекс (mutex) – примитив синхронизации для организации в программах критических секций –
взаимного исключения (mutual exclusion) выполнения заданного участка кода в один момент
времени более одним потоком
double s = 0.0; // разделяемая переменная
pthread_mutex_t mutex_sum = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_lock(&mutex_sum);
s = s + val;
pthread_mutex_unlock(&mutex_sum);
▪ lock/acquire – захват мьютекса осуществляется только одним потоком, остальные ожидают его
освобождения
▪ unlock/release – освобождение мьютекса
Интегрирование методом средних прямоугольников
double s = 0.0;
pthread_mutex_t mutex_sum = PTHREAD_MUTEX_INITIALIZER;
void *integrate(void *arg)
{
struct thread_data *p = (struct thread_data *)arg;
double h = (b - a) / n;
int points_per_proc = n / p->nthreads;
int lb = p->threadno * points_per_proc;
int ub = (p->threadno == p->nthreads - 1) ? (n - 1) : (lb + points_per_proc - 1);
double locs = 0.0;
for (int i = lb; i < ub; i++)
locs += func(a + h * (i + 0.5));
// Update global sum
locs *= h;
pthread_mutex_lock(&mutex_sum);
s += locs;
// Критическая секция
pthread_mutex_unlock(&mutex_sum);
return NULL;
}
./integrate
Numerical integration (2 threads): [-4.000000, 4.000000], n = 10000000
Result Pi: 3.141589720795
Elapsed time (sec.): 0.151318
$ ./integrate
Numerical integration (2 threads): [-4.000000, 4.000000], n = 10000000
Result Pi: 3.141589720795
Elapsed time (sec.): 0.152941
Интегрирование методом средних прямоугольников
double s = 0.0;
pthread_mutex_t mutex_sum = PTHREAD_MUTEX_INITIALIZER;
void *integrate(void *arg)
{
struct thread_data *p = (struct thread_data *)arg;
double h = (b - a) / n;
int points_per_proc = n / p->nthreads;
int lb = p->threadno * points_per_proc;
int ub = (p->threadno == p->nthreads - 1) ? (n - 1) : (lb + points_per_proc - 1);
double locs = 0.0;
for (int i = lb; i < ub; i++)
locs += func(a + h * (i + 0.5));
// Update global sum
locs *= h;
pthread_mutex_lock(&mutex_sum);
s += locs;
// Критическая секция
pthread_mutex_unlock(&mutex_sum);
return NULL;
}
./integrate
Numerical integration (2 threads): [-4.000000, 4.000000], n = 10000000
Result Pi: 3.141589720795
Elapsed time (sec.): 0.151318
$ ./integrate
Numerical integration (2 threads): [-4.000000, 4.000000], n = 10000000
Result Pi: 3.141589720795
Elapsed time (sec.): 0.152941
Информация о системе: процессоры
$ cat /proc/cpuinfo
processor : 0
cpu family
: 6
model
: 23
model name
: Intel(R) Xeon(R) CPU
E5420 @ 2.50GHz
stepping : 10
microcode : 0xa0b
cpu MHz
: 1998.000
cache size
: 6144 KB
physical id
: 0
siblings : 4
core id
: 0
cpu cores : 4
apicid
: 0
initial apicid
: 0
fpu
: yes
fpu_exception
: yes
cpuid level
: 13
wp
: yes
flags
: fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx
fxsr sse sse2 ss ht tm pbe syscall nx lm constant_tsc arch_perfmon pebs bts rep_good nopl aperfmperf pni dtes64
monitor ds_cpl vmx est tm2 ssse3 cx16 xtpr pdcm dca sse4_1 xsave lahf_lm dtherm tpr_shadow vnmi flexpriority
bogomips : 4987.59
clflush size
: 64
cache_alignment
: 64
address sizes
: 38 bits physical, 48 bits virtual
Информация о системе: процессоры
$ ls /sys/devices/system/cpu
drwxr-xr-x
drwxr-xr-x
drwxr-xr-x
drwxr-xr-x
drwxr-xr-x
drwxr-xr-x
drwxr-xr-x
drwxr-xr-x
drwxr-xr-x
drwxr-xr-x
-r--r--r-drwxr-xr-x
-r--r--r--r--r--r--r--r--r--r--r--r-drwxr-xr-x
-r--r--r---w--------w-------rw-r--r--
9
8
8
8
8
8
8
8
3
2
1
2
1
1
1
1
2
1
1
1
1
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
root
4096
4096
4096
4096
4096
4096
4096
4096
4096
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
Feb
9
9
9
9
9
9
9
9
12
12
12
12
12
12
9
9
12
12
12
12
9
13:34
13:34
13:34
13:34
13:34
13:34
13:34
13:34
18:23
18:23
18:23
18:23
18:23
18:23
13:34
18:13
18:23
18:23
18:23
18:23
13:34
cpu0
cpu1
cpu2
cpu3
cpu4
cpu5
cpu6
cpu7
cpufreq
cpuidle
kernel_max
microcode
modalias
offline
online
possible
power
present
probe
release
uevent
Информация о системе: процессоры
$ dmesg | grep CPU
[
0.000000]
[
0.000000]
[
0.000000]
[
0.000000]
[
0.000000]
[
0.010784]
[
0.010785]
[
0.010788]
[
0.010796]
[
0.033284]
stepping: 0a)
[
0.047160]
[
0.180019]
[
3.382722]
[
3.873633]
[
3.873695]
[
3.873747]
[
3.873783]
[
3.873823]
[
3.873865]
[
3.873922]
smpboot: Allowing 8 CPUs, 0 hotplug CPUs
setup_percpu: NR_CPUS:1024 nr_cpumask_bits:1024 nr_cpu_ids:8 nr_node_ids:1
PERCPU: Embedded 29 pages/cpu @ffff88025fc00000 s86784 r8192 d23808 u262144
SLUB: HWalign=64, Order=0-3, MinObjects=0, CPUs=8, Nodes=1
RCU restricting CPUs from NR_CPUS=1024 to nr_cpu_ids=8.
CPU: Physical Processor ID: 0
CPU: Processor Core ID: 0
mce: CPU supports 6 MCE banks
CPU0: Thermal monitoring enabled (TM2)
smpboot: CPU0: Intel(R) Xeon(R) CPU
E5420 @ 2.50GHz (fam: 06, model: 17,
NMI watchdog: enabled on all
Brought up 8 CPUs
microcode: CPU0 sig=0x1067a,
microcode: CPU1 sig=0x1067a,
microcode: CPU2 sig=0x1067a,
microcode: CPU3 sig=0x1067a,
microcode: CPU4 sig=0x1067a,
microcode: CPU5 sig=0x1067a,
microcode: CPU6 sig=0x1067a,
microcode: CPU7 sig=0x1067a,
CPUs, permanently consumes one hw-PMU counter.
pf=0x40,
pf=0x40,
pf=0x40,
pf=0x40,
pf=0x40,
pf=0x40,
pf=0x40,
pf=0x40,
revision=0xa0b
revision=0xa0b
revision=0xa0b
revision=0xa0b
revision=0xa0b
revision=0xa0b
revision=0xa0b
revision=0xa0b
Ресурсы системы
$ lstopo
Machine (7980MB)
Socket L#0
L2 L#0 (6144KB)
L1d L#0 (32KB) + L1i
L1d L#1 (32KB) + L1i
L2 L#1 (6144KB)
L1d L#2 (32KB) + L1i
L1d L#3 (32KB) + L1i
Socket L#1
L2 L#2 (6144KB)
L1d L#4 (32KB) + L1i
L1d L#5 (32KB) + L1i
L2 L#3 (6144KB)
L1d L#6 (32KB) + L1i
L1d L#7 (32KB) + L1i
HostBridge L#0
PCIBridge
PCIBridge
PCIBridge
PCI 8086:107d
Net L#0 "p3p1"
PCIBridge
PCI 8086:1096
Net L#1 "em1"
PCI 8086:1096
Net L#2 "em2"
PCIBridge
PCI 1002:515e
...
L#0 (32KB) + Core L#0 + PU L#0 (P#0)
L#1 (32KB) + Core L#1 + PU L#1 (P#4)
L#2 (32KB) + Core L#2 + PU L#2 (P#1)
L#3 (32KB) + Core L#3 + PU L#3 (P#5)
L#4 (32KB) + Core L#4 + PU L#4 (P#2)
L#5 (32KB) + Core L#5 + PU L#5 (P#6)
L#6 (32KB) + Core L#6 + PU L#6 (P#3)
L#7 (32KB) + Core L#7 + PU L#7 (P#7)
Ресурсы системы: память
$
free
total
Mem:
16429124
-/+ buffers/cache:
Swap:
18108436
used
15327724
2279928
1279484
free
1101400
14149196
16828952
$ cat /sys/devices/system/node/node0/meminfo
Node 0 MemTotal:
16429124 kB
Node 0 MemFree:
1097292 kB
Node 0 MemUsed:
15331832 kB
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Node
Active:
7235284 kB
Inactive:
7125736 kB
Active(anon):
200724 kB
Inactive(anon): 1113492 kB
Active(file):
7034560 kB
Inactive(file): 6012244 kB
Unevictable:
0 kB
Mlocked:
0 kB
Dirty:
252 kB
Writeback:
0 kB
FilePages:
14127436 kB
Mapped:
712380 kB
AnonPages:
262700 kB
Shmem:
1056 kB
KernelStack:
4544 kB
PageTables:
58760 kB
NFS_Unstable:
0 kB
Bounce:
0 kB
WritebackTmp:
0 kB
Slab:
758652 kB
SReclaimable:
707824 kB
SUnreclaim:
50828 kB
AnonHugePages:
34816 kB
shared
1056
buffers
361236
cached
12686560
Литература
1. Blaise Barney. POSIX Threads Programming // https://computing.llnl.gov/tutorials/pthreads/
2. POSIX thread (pthread) libraries // http://www.cs.cmu.edu/afs/cs/academic/class/15492-f07/www/pthreads.html
3. Эхтер Ш., Робертс Дж. Многоядерное программирование. – СПб.: Питер, 2010. – 316 с.
4. Эндрюс Г.Р. Основы многопоточного, параллельного и распределенного программирования. – М.: Вильямс,
2003. – 512 с.
5. Darryl Gove. Multicore Application Programming: for Windows, Linux, and Oracle Solaris. – Addison-Wesley, 2010.
– 480 p.
6. Maurice Herlihy, Nir Shavit. The Art of Multiprocessor Programming. – Morgan Kaufmann, 2008. – 528 p.
7. Richard H. Carver, Kuo-Chung Tai. Modern Multithreading : Implementing, Testing, and Debugging Multithreaded
Java and C++/Pthreads/Win32 Programs. – Wiley-Interscience, 2005. – 480 p.
8. Anthony Williams. C++ Concurrency in Action: Practical Multithreading. – Manning Publications, 2012. – 528 p.
9. Träff J.L. Introduction to Parallel Computing // http://www.par.tuwien.ac.at/teach/WS12/ParComp.html