Многопоточность замедляет мой код, хотя я обратил внимание на следующие сообщения:
Многопоточный GEMM медленнее однопоточного?
Почему эта OpenMP-программа работает медленнее, чем однопоточная?
Я думаю, что все меры предосторожности были соблюдены:
Мой процессор4 ядра + гиперпоточность (8 эффективно), и я не запускаю более 4 потоков
Количество векторных записей, над которыми работает каждый поток, кажется достаточно большим (2 миллиона на поток). Следовательно, любое ложное совместное использование (проблема со строкой кэша) должно быть незначительным , поскольку большинство данных не пересекаются с данными других потоков.
Записи в памяти являются последовательными,вероятность пропуска кэша очень мала.
с использованием переменной tmp
для последовательных операций вместо назначения значений непосредственно в массив.
Сборка в режиме релиза, visual studio
Критических точек между потоками нет (они не используют мьютексы и не обмениваются данными)
При измерении времени я включаю создание потока.Конечно, запуск 4 потоков не может быть таким дорогим?
1 поток: около 140 миллисекунд
4 потока: около 155 миллисекунд
Main:
struct MyStruct {
double val = 0;
};
size_t numEntries = 100e4;
size_t numThreads = 4;
std::vector<MyStruct> arr;
void main(){
arr.reserve(numEntries);
for(size_t i=0; i<numEntries; ++i){
MyStruct m{ i };
arr.push_back(m);
}
//run several times
float avgTime=0;
for(size_t n=0; n<100; ++n){
launchThreads(avgTime);
//space out to make avgTime more even:
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
avgTime /= 100;
std::cout << "finished in " << avgTime <<"milliseconds\n";
system("pause");
}
Запуск и запуск потоков:
//ran by each thread
void threadWork(size_t threadId){
size_t numPerThread = (numEntries+numThreads -1) / numThreads;
size_t start_ix = threadId * numPerThread;
size_t endIx;
if (threadId == numThreads - 1) {
endIx = numEntries-1;//we are the last thread
}
else {
endIx = start_ix + numPerThread;
}
for(size_t i=5; i<endIx-5; ++i){
double tmp = arr[i].val;
tmp += arr[i-1].val;
tmp += arr[i-3].val;
tmp += arr[i-4].val;
tmp += arr[i-5].val;
tmp += arr[i-2].val;
tmp += arr[i+1].val;
tmp += arr[i+3].val;
tmp += arr[i+4].val;
tmp += arr[i+5].val;
tmp += arr[i+2].val;
if(tmp > 0){ tmp *= 0.5f;}
else{ tmp *= 0.3f; }
arr[i].val = tmp;
}
}//end()
//measures time
void launchThreads(float &avgTime){
using namespace std::chrono;
typedef std::chrono::milliseconds ms;
high_resolution_clock::time_point t1 = high_resolution_clock::now();
std::vector<std::thread> threads;
for (int i = 0; i <numThreads; ++i) {
std::thread t = std::thread(threadWork, i);
threads.push_back(std::move(t));
}
for (size_t i = 0; i < numThreads; ++i) {
threads[i].join();
}
high_resolution_clock::time_point t2 = high_resolution_clock::now();
ms timespan = duration_cast<ms>(t2 - t1);
avgTime += timespan.count();
}