여러개의 명령어를 동시에 처리하기위해 파이프라인 및 연산 유닛을 다중화, 명령어 재배치
파이프 라인의 소요시간을 조절해서 병렬성을 높임
실행시간, 메모리 사용량을 줄이기 위한 기법 주로 불필요한 연산을 줄임.
컴파일 타임에 상수 값을 결정하기, 순서 바꾸기 등기, 순서 바꾸기 등
위에서 살펴본 컴퓨터 구조 때문에, 각 명령어들을 재배치해서 최적화함
다음 예시를 살펴보자
#include <thread>
#include <atomic>
int32 x = 0;
int32 y = 0;
int32 r1 = 0;
int32 r2 = 0;
volatile bool ready;
void Thread1()
{
while (!ready);
y = 1;
r1 = x;
}
void Thread2()
{
while (!ready);
x = 1;
r2 = y;
}
int main()
{
int32 count = 0;
while (true)
{
ready = false;
count++;
x = y = r1 = r2 = 0;
thread t1(Thread1);
thread t2(Thread2);
ready = true;
t1.join();
t2.join();
if (r1 == 0 && r2 == 0)
break;
}
cout << count << endl;
}
싱글 스레드 관점에서 살펴보면 위 코드는 종료되지 않고 무한 루프를 돌아야 한다.
하지만 멀티 스레드에서는 변수의 가시성, 명령어 재배치 등에 의해서 종료되는 결과를 보여준다.
#include <thread>
#include <atomic>
atomic<bool> ready = false;
int32 value;
//같은 경우도 사실은 memory_order의 기본 값을 통해서 함수를 호출하고 있었던 것
void Producer()
{
value = 10;
ready.store(true, memory_order::memory_order_seq_cst);
//ready.store(true, memory_order::memory_order_release); // 이 위의 명령어는 아래로 재배치 금지
//ready.store(true, memory_order::memory_order_relaxed);
// atomic 없이 사용하려면
std::atomic_thread_fence(memory_order_release);
}
void Consumer()
{
while (ready.load(memory_order::memory_order_seq_cst) == false);
//while (ready.load(memory_order::memory_order_acquire) == false);// 이 아래의 명령어는 위로 재배치 금지, release 이후의 변경사항이 모두 반영
//while (ready.load(memory_order::memory_order_relaxed) == false);
cout << value;
}
int main()
{
ready = false;
value = 0;
thread t1(Producer);
thread t2(Consumer);
t1.join();
t2.join();
}
#include <thread>
#include <atomic>
#include <chrono>
thread_local int32 LThreadId = 0;
void ThreadMain(int32 threadId)
{
LThreadId = threadId;
while (true)
{
printf("Hi I am Thread %d \n", LThreadId);
//cout << "Hi I am Thread " << LThreadId << endl;
this_thread::sleep_for(1s);
}
}
int main()
{
vector<thread> threads;
for (int i = 0; i < 10; i++)
{
int32 threadId = i + 1;
threads.push_back(thread(ThreadMain, threadId));
}
for (auto& t : threads)
t.join();