LINUX.ORG.RU

C++ — многопоточность


0

0

Есть некоторая функция double a(int x). Выполняется она довольно долго, причём для разных иксов — разное время.

Нужно эту a(x) профигачить 1000 раз (для x=0..999) и записать то, что она возвращала, в массив mass_a. Так, чтобы mass_a[0] было равно a(0), mass_a[1]— a(1) и т.д.

Как сделать так, чтобы софтина выполнялась в несколько потоков на мультипроцессорных системах? Ведь пока одно ядро считает a(0), второе может считать a(1) — скорость работы кода возрастает в два раза. Вроде бы как, такое делают через pthreads (причём оно на C), но ещё Boost предоставляет что-то подобное, в общем я толком не разобрался.

Хотелось бы, чтобы число потоков можно было задавать переменной.

Короче, как такое обычно делают, и насколько сильно вырастет код? Да, и придётся ли как-то обезопасить массив mass_a от одновременной записи с обоих потоков, или никакого вреда от этого нет (не то что в случае работы с файлом)?

★★★★★

#include <omp.h>

int main() {
omp_set_num_threads(my_favorite_number_of_threads);
#pragma omp parallel for
for(int i = 0;i < 1000;i++)
mass_a[i] = a(i);
return 0;
}

gcc -fopenmp -lgomp file.cpp

YesSSS ★★★
()
Ответ на: комментарий от Obey-Kun

Назначение разное, OpenMP - быстрое распараллеливание простых вычислительных задач, Intel TBB - если задачи более сложные(e.g. зависимости между вычислениями) и код хочется иметь более структурированный и ооп-шный, pthreats - для любителей low-level ну и т.д.

YesSSS ★★★
()

Ман по pthread_create раскуривается за пять минут (если есть навыки в си), объяснять дольше получится.

staseg ★★★★★
()
Ответ на: комментарий от YesSSS

Хм, неплохо.

http://software.intel.com/ru-ru/articles/threading-models-for-high-performance-computing-pthreads-or-openmp/

Интерфейс OpenMP удобен, потому что он не ограничивает ПО заранее заданным количеством потоков. Такой способ привязки представляет большую проблему для многопоточных приложений, которые используют низкоуровневые программные интерфейсы – Pthreads и Win32. Как может приложение, написанное с такими программными интерфейсами, масштабировать количество потоков при выполнении на платформе с большим числом процессоров? Один из подходов заключается в использовании пулов потоков, в которых при запуске программы создается множество потоков и между ними распределяется вся работа. Однако этот подход требует объемного специального кода многопоточности, и нет гарантии того, что масштабирование произойдет самым оптимальным способом с учетом количества процессоров. При использовании OpenMP это число не требуется указывать.

Прагмы OpenMP имеют еще одно ключевое преимущество: с помощью отключения поддержки OpenMP код может быть скомпилирован как однопоточное приложение. Компиляция кода таким способом может быть существенным преимуществом при отладке программы. При отсутствии такой возможности разработчики часто не могут определить, почему неправильно работает сложный код: из-за проблемы многопоточности или из-за ошибки при разработке, не имеющей отношения к многопоточности.

http://ompf.org/forum/viewtopic.php?f=4&t=589

Well, OpenMP requires a compiler that supports it ( for example, the Visual Studio 2005 express edition does support it but doesn´t come with the required header files and libs).

Boost on the other hand is still pretty low level, requiring much more thinking on how to do thing right.

  • Лёгок для использования.
  • Позволяет легко выбрать кол-во потоков.
  • Код, его использующий, очень лаконичен.
  • Будет работать и на винде (что мне, впрочем, вряд ли пригодится, но мало ли).
  • Его легко прикрутить к уже существующему коду.

Т.к. мне нужно именно распараллеливание, а не гетерогенное программирование (или как там это называется?), то мне именно OpenMP и нужно, видимо. Спасибо.

Obey-Kun ★★★★★
() автор топика
Ответ на: комментарий от YesSSS

Ясно. Ну, мне-то только распараллелить, так что OpenMP хватит по горло. Спасибо.

Obey-Kun ★★★★★
() автор топика
#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/locks.hpp>
#include <boost/bind.hpp>

int a(int x)
{
	return x*x;
}

int next_i(int n, int & i, boost::mutex & m)
{
	boost::unique_lock<boost::mutex> lock(m);
	if(i >= n)
		return -1;
	return i++;
}

void worker(std::vector<double> & mass_a, boost::function<int()> f)
{
	for(;;)
	{
		int cur_i = f();
		if(cur_i < 0)
			break;
		mass_a[cur_i] = a(cur_i);
	}
}

int main()
{
	int i = 0;
	boost::mutex m;
	std::vector<double> mass_a;
	mass_a.resize(1000);
	boost::function0<int> f = boost::bind(&next_i, mass_a.size(), boost::ref(i), boost::ref(m));
	boost::thread_group threads;
	for (int i=0; i<boost::thread::hardware_concurrency(); ++i)
		threads.create_thread(boost::bind(&worker, boost::ref(mass_a), f));
	threads.join_all();
	return 0;
}
stpg
()

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

kto_tama ★★★★★
()
Ответ на: комментарий от Obey-Kun

Если программируешь на Qt, то там кроме низкоуровневых потоков недавно добавили Concurrency Framework, предназначеный как раз для твоей задачи. Конкретно смотреть классы: QtConcurrency, QRunnable, QThreadPool.

Dendy ★★★★★
()
Ответ на: комментарий от dizza

> Едрить-колотить...

Согласен, можно было бы обойтись атомарным счетчиком вместо мютекса, но к сожалению оного нет в стандарте или бусте=)

stpg
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.