많은 논쟁이있을 때 lock free 구조가 더 좋으며, 경합이 낮 으면 잠긴 데이터 구조가 더 잘된다는 것을 이해합니다.lockfree와 locked 데이터 구조를 벤치 마크하는 올바른 방법
#include<thread>
#include<chrono>
#include<iostream>
#include<vector>
#include<stack>
#include<mutex>
#include<fstream>
#include <boost/lockfree/stack.hpp>
using namespace std;
mutex mut;
const static int totalNumberOfWorkItems = 100000;
const static int maxNumberOfThreads = 2000;
const static int threadIncrement = 5;
chrono::milliseconds calcRawSpawnTime(int n) {
auto start = chrono::high_resolution_clock::now();
vector<thread> ts;
int j = 0;
for (int i = 0; i < n; i++)
ts.push_back(thread([&](){j += i; }));
for (auto&& t : ts)
t.join();
auto end = chrono::high_resolution_clock::now();
return chrono::duration_cast<chrono::milliseconds>(end - start);
}
chrono::milliseconds timeNThreadsLock(int n, int worksize){
stack<int> data;
vector<thread> ts;
auto startSpawn = chrono::high_resolution_clock::now();
for (int i = 0; i < n; i++)
ts.push_back(thread([&]() {
for (int j = 0; j < worksize; j++){
mut.lock();
data.push(7);
mut.unlock();
}
}));
auto startWait = chrono::high_resolution_clock::now();
for (auto&& t : ts)
t.join();
auto endWait = chrono::high_resolution_clock::now();
return chrono::duration_cast<chrono::milliseconds>(endWait - startSpawn);
}
chrono::milliseconds timeNThreadsLockFree(int n, int worksize)
{
boost::lockfree::stack<int> data;
vector<thread> ts;
auto startSpawn = chrono::high_resolution_clock::now();
for (int i = 0; i < n; i++)
ts.push_back(thread([&](){
for (int j = 0; j < worksize; j++)
data.push(7);
}));
auto startWait = chrono::high_resolution_clock::now();
for (auto&& t : ts)
t.join();
auto endWait = chrono::high_resolution_clock::now();
return chrono::duration_cast<chrono::milliseconds>(endWait - startSpawn);
}
int main(int argc, char* argv [])
{
ofstream lockFile("locklog.log");
ofstream lockFreeFile("lockfreelog.log");
ofstream spawnTimes("spawnTimes.log");
for (int i = 1; i < maxNumberOfThreads; i += threadIncrement){
cout << i << endl;
spawnTimes << i << ",\t" << calcRawSpawnTime(i).count() << endl;
lockFreeFile << i << ",\t" << timeNThreadsLockFree(i, totalNumberOfWorkItems/i).count() << endl;
lockFile << i << ",\t" << timeNThreadsLock(i, totalNumberOfWorkItems/i).count() << endl;
}
return 0;
}
문제는 내 lockfree 데이터 구조 시간은 다음과 같이 시작이다 :
이를 테스트하려면
, 나는 다음과 같은 코드를 썼습니다. 가 나는 문제가 (분명히 일정하지 더 스레드) 스레드 생성 시간이었다 의심하지만, 스레드 생성 시간을 뺀 것은이 플롯했다 : 분명히 잘못![enter image description here](https://i.stack.imgur.com/s9pdn.png)
합니다.
이것을 올바르게 벤치마킹하는 방법에 대한 아이디어가 있습니까?
적어도 전체적인 문제를 피하기 위해 벤치 마크 코드 외부에 스레드를 생성합니다. 무시할 수있는 양의 작업을 고려할 때, 스레드 생성 시간이 분명히 지배적입니다 (두 번째 플롯에서 쓰레드 생성 시간을 어떻게 빼는 지 모르시겠습니까?) – Voo
@Voo 어떻게 쓰레드를 만들 수 있습니까? 잠긴 버전의 경우 모든 스레드가 생성 될 때까지 단순히 뮤텍스를 잠글 수 있습니다. 그런 다음 스레드를 잠금 해제하면 모든 스레드가 스레드 풀을 사용할 수 있습니다. 잠금이없는 버전의 경우 동일한 작업을 수행하기 위해 조건 변수를 사용하려고 시도했지만 합계가 너무 낮아서 (~ 4ms) 타이밍이 흐려지고 있습니다. 아이디어? – soandos
두 소리에 대한 조건 변수는 오른쪽에 있고, 벡터에 대한 밀어 넣기는 일반적인 경우에는 하나의 연동 추가에 지나지 않습니다. 또한 각 스레드가 수천 개의 요소를 순차적으로 추가하여 작업량을 늘리는 것이 좋습니다. 타이머 정확도는 종종 10ms 범위에 불과합니다. 나중에 합계를 더하고 컴파일하여 컴파일러 최적화가 당신과 관계가 없는지 확인하십시오. – Voo