• 카테고리

    질문 & 답변
  • 세부 분야

    프로그래밍 언어

  • 해결 여부

    해결됨

(과제스포)제가 과제를 똑바로 이해했는지 궁금합니다.

24.03.05 17:28 작성 24.03.05 17:31 수정 조회수 100

1

#include<chrono>
#include<iostream>
#include<mutex>
#include<random>
#include<thread>
#include<utility>
#include<vector>
#include<atomic>
#include<future>
#include<numeric>
#include<execution>

using namespace std;

mutex mtx;


void dotProductDQThread(const vector<int>& v0, const vector<int>& v1,
	const unsigned i_start, const unsigned i_end, unsigned long long& sum)
{
	int sum_tmp = 0; //local sum
	for (unsigned i = i_start; i < i_end; ++i)
	{
		sum_tmp += v0[i] * v1[i];
	}
	sum += sum_tmp;
}

void dotProductProm(const vector<int>& v0, const vector<int>& v1,
	const unsigned i_start, const unsigned i_end, promise<unsigned long long>&& sum)
{
	int sum_tmp = 0; //local sum
	for (unsigned i = i_start; i < i_end; ++i)
	{
		sum_tmp += v0[i] * v1[i];
	}
	sum.set_value(sum_tmp);
}



int main()
{
	/*v0 = { 1,2,3 };
	v1 = { 4,5,6 };
	v0_dot = 1 * 4 + 2 * 5 + 3 * 6;*/

	const long long n_data = 100'000'000;
	const unsigned n_threads = 4;

	//initialize vectors
	std::vector<int> v0, v1;
	v0.reserve(n_data);
	v1.reserve(n_data);

	random_device seed;
	mt19937 engine(seed());

	uniform_int_distribution<> uniformDist(1, 10);

	//v0와 v1에 값 무작위로 넣어줌
	for (long long i = 0; i < n_data; ++i)
	{
		v0.push_back(uniformDist(engine));
		v1.push_back(uniformDist(engine));
		//cout << v0[i] << "\t" << v1[i] << endl;
	}


	cout << "std::inner_product" << endl;
	{
		const auto sta = chrono::steady_clock::now();
		const auto sum = std::inner_product(v0.begin(), v0.end(), v1.begin(), 0ull);//0ull = unsigned longlong 0
		const chrono::duration<double> dur = chrono::steady_clock::now() - sta;

		cout << dur.count() << endl;
		cout << sum << endl;
		cout << endl;
	}


	//TODO: use divde and conquer strategy for std::thread
	cout << "TODO: use divde and conquer strategy for std::thread" << endl;
	{
		const auto sta = chrono::steady_clock::now();
		unsigned long long sum = 0;

		vector<thread> threads;
		threads.resize(n_threads);

		const unsigned n_per_thread = n_data / n_threads;
		for (unsigned t = 0; t < n_threads; ++t)
			threads[t] = std::thread(dotProductDQThread, std::ref(v0), std::ref(v1),
				t * n_per_thread, (t + 1) * n_per_thread, std::ref(sum));

		for (unsigned t = 0; t < n_threads; ++t)
			threads[t].join();

		const chrono::duration<double> dur = chrono::steady_clock::now() - sta;
		cout << dur.count() << endl;
		cout << sum << endl;
		cout << endl;
	}

	//TODO: use promise
	cout << "TODO: use promise" << endl;
	{
		const auto sta = chrono::steady_clock::now();
		//std::promise<unsigned long long> sum;
		
		auto sum = 0ull;
		vector<thread> threads;
		vector<future<unsigned long long>> futures;
		vector<promise<unsigned long long>> proms;
		threads.resize(n_threads);
		futures.resize(n_threads);
		proms.resize(n_threads);

		const unsigned n_per_thread = n_data / n_threads;
		for (unsigned t = 0; t < n_threads; ++t)
		{
			futures[t] = proms[t].get_future();
			threads[t] = std::thread(dotProductProm, std::ref(v0), std::ref(v1),
				t * n_per_thread, (t + 1) * n_per_thread, std::move(proms[t]));
		}

		for (unsigned t = 0; t < n_threads; ++t)
		{
			threads[t].join();
			sum += futures[t].get();
		}


		const chrono::duration<double> dur = chrono::steady_clock::now() - sta;
		cout << dur.count() << endl;
		cout << sum << endl;
		cout << endl;
	}

}

결과는 제대로 나오지만, 제가 과제를 똑바로 이해했는지 궁금해서 여쭤봅니다.

  1. 과제1: use divde and conquer strategy for std::thread
    쓰레드에 sum에 local sum값을 넣어 race condition 해결

  2. 과제2: prom 사용해보기
    sum 변수 선언 및 thread, promise, future 모두 쓰레드 크기 만큼의 vector로 만들어줬습니다.
    병렬 처리 후 future값을 sum에 더해줬습니다.

    ※추가 궁금증
    promise 과제 중, std::ref와 std::move 둘 다 해보았습니다. 두 경우 모두 정상 작동하였는데, 어떤 방법을 가장 추천하시나요?

답변 1

답변을 작성해보세요.

1

Soobak님의 프로필

Soobak

2024.03.06

안녕하세요, 질문&답변 도우미 Soobak 입니다.

 

과제 1)
: 여러 스레드를 통한 병렬 처리 작업에 대해서는 잘 작성하셨지만, sum += sum_tmp 에 대해서 sum 에 대한 race condition 을 방지하기 위한 mutex 의 사용을 누락하신 것 같습니다.
mutex mtx; 의 정의는 되어있지만, mtx 를 사용하신 곳이 없습니다.
이 부분에 대해서 점검하시어, 각 스레드가 sum 에 값을 추가하기 전에 mutex 로 접근을 제어하여 보호하는 방법을 생각해보시면 좋을 것 같습니다.

 

과제 2)
: promisefuture 에 대해서 잘 이해하시고, race condition 을 방지하며 올바르게 합계를 계산하도록 잘 작성하신 것 같습니다. 👍

 

추가적으로, std::ref 는 참조자(wrapper)를 생성하여 함수나 스레드 생성자로 '객체를 참조로 전달할 때' 사용합니다. 따라서, 객체를 복사하지 않고 참조를 전달하고자 할 때 유용합니다.

std::move 는 '객체의 소유권을 이동' 시키는 데에 사용됩니다. 주로 객체의 복사를 피하고 리소스를 효율적으로 이전하고자 할 때 사용됩니다.

위 두 개념을 잘 비교하시어 이해해보시면 학습에 보다 더 도움이 되실 것 같습니다.

이 때, promise 객체의 경우, 복사는 할 수 없으며 오직 이동만 가능하기 때문에, std::move 를 사용하여 promise 객체를 스레드 함수로 전달하는 것이 적절합니다.

전반적으로 잘 작성하셨으며, 과제 1 에서 race condition 을 해결하기 위한 mutex 사용이 누락된 부분만 보완하시면 좋을 것 같습니다.

👍