나 여기 처음이야실, 저는 다음을 사용하여 이미지를 만드는 간단한 작업을 통해 새로 습득한 기술을 테스트하고 싶었습니다.멀티스레딩, 흥미로운 부분은,단일 스레드에서는 4개의 스레드를 사용하는 것보다 프로그램이 더 빠르게 실행됩니다.(나는 이것이 나의 가장 효율적인 병렬 스레딩 기능이라고 생각합니다.) 나는 ubuntu 17을 사용하는 i3 프로세서를 가지고 있고std::thread::hardware_concurrency는 4입니다.. 내 코드:
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <png++/png.hpp>
#include <time.h>
std::vector<int> bounds(int max, int parts)
{
std::vector<int> interval;
int gap = max / parts;
int left = max % parts;
int nr1 = 0;
int nr2;
interval.push_back(nr1);
for (int i = 0; i < parts; i++)
{
nr2 = nr1 + gap;
if (i == parts - 1)
nr2 += left;
nr1 = nr2;
interval.push_back(nr2);
}
return interval;
}
void create_image(png::image<png::rgb_pixel> &image, int start, int end)
{
std::mutex my_mutex;
std::lock_guard<std::mutex> locker(my_mutex);
srand(time(NULL));
for (int i = start; i < end; i++)
for (int j = 0; j < image.get_height(); j++)
image[i][j] = png::rgb_pixel(rand() % 256, 0, rand() % 256);
}
int main()
{
png::image<png::rgb_pixel> png_image(6000, 6000); //Creating Image
int parts = 1; //amount of parallel threads
std::vector<int> my_vector = bounds(png_image.get_width(), parts); //interval vector
std::vector<std::thread> workers; //threads
time_t start, end;
time(&start); //measuring time
for (int i = 0; i < parts - 1; i++)
{
workers.push_back(std::thread(create_image, std::ref(png_image), my_vector[i], my_vector[i + 1]));
}
for (int i = 0; i < parts - 1; i++)
workers[i].join();
create_image(png_image, my_vector[parts - 1], my_vector[parts]);
png_image.write("test.png");
time(&end);
std::cout << (end - start) << " seconds\n";
return 0;
}
빌드하려면 다음을 실행하세요 g++ file.cpp -o test -lpng -pthread
(다음을 사용하여PNG++).
답변1
뮤텍스는 빨간 청어입니다. 이는 함수에 로컬이므로 각 스레드가 별도의 뮤텍스로 끝나기 때문에 실제로 아무것도 잠그지 않습니다. 실제로 잠그려면 create_image에서 뮤텍스 변수를 이동해야 합니다.
그러나 이미지에 대한 쓰기는 독립적이므로 실제로 잠금이 필요하지 않습니다. 즉, create_image에 대한 각 호출은 별도의 영역에 대한 것이므로 쓰기가 겹치지 않습니다. 스레드에 참여하고 완료될 때까지 기다리면 변경 사항이 기록되도록 보장할 수 있습니다.
문제는 실제로 rand()입니다. 내 테스트에 따르면 자체 내부 뮤텍스가 있어 모든 속도 저하가 발생합니다. rand()에서 rand_r(&seed)로 변경하면 큰 차이가 발생합니다. 사용하는 스레드가 많을수록 (호출당) 잠금 비용이 더 많이 들기 때문에 속도가 느려지는 것을 볼 수 있습니다.
하지만 내 CPU에서는 PNG 생성이 프로그램의 주요 비용입니다. PNG 이미지를 작성하지 않으면 프로그램이 2초 이내에 실행되며(단일 스레드) 사용된 코어 수에 따라 거의 선형적으로 확장됩니다. PNG 이미지를 작성하면 시간이 8초 이상으로 뛰기 때문에 이미지를 만드는 것보다 PNG 이미지를 작성하는 데 시간이 훨씬 오래 걸립니다.
이것이 내가 생각해낸 것입니다:
#include <iostream>
#include <vector>
#include <thread>
#include <mutex>
#include <png++/png.hpp>
#include <time.h>
std::vector<int> bounds(int max, int parts)
{
std::vector<int> interval;
int gap = max / parts;
int left = max % parts;
int nr1 = 0;
int nr2;
interval.push_back(nr1);
for (int i = 0; i < parts; i++)
{
nr2 = nr1 + gap;
if (i == parts - 1)
nr2 += left;
nr1 = nr2;
interval.push_back(nr2);
}
return interval;
}
void create_image(png::image<png::rgb_pixel> &image, int start, int end)
{
unsigned int seed = time(NULL);
for (int i = start; i < end; i++)
for (int j = 0; j < image.get_height(); j++)
image[i][j] = png::rgb_pixel(rand_r(&seed) % 256, 0, rand_r(&seed) % 256);
}
int main()
{
png::image<png::rgb_pixel> png_image(6000, 6000); //Creating Image
int parts = 1; //amount of parallel threads
std::vector<int> my_vector = bounds(png_image.get_width(), parts); //interval vector
std::vector<std::thread> workers; //threads
time_t start, end;
time(&start); //measuring time
for (int i = 0; i < parts; i++)
{
workers.push_back(std::thread(create_image, std::ref(png_image), my_vector[i], my_vector[i + 1]));
}
for (int i = 0; i < parts; i++)
workers[i].join();
png_image.write("test.png");
time(&end);
std::cout << (end - start) << " seconds\n";
return 0;
}