You are here

LabVIEW와 하이퍼스레딩

이퍼스레딩은 인텔 펜티엄 4 및 후속 버전의 CPU에 제공되는 고급 기능입니다. 하이퍼스레딩을 지원하는 컴퓨터는 단 하나의 프로세서가 장착되었음에도 여러 프로세서가 장착된 것과 같은 기능을 할 수 있습니다. 하이퍼스레딩 지원 컴퓨터에서 윈도우 작업 관리자를 실행하여 성능 탭을 클릭하면 CPU 2개에 대한 점유율이 나타납니다.

목차

  • LabVIEW와 하이퍼스레딩
  • LabVIEW 실행 개요
  • 소수 병렬수행 예제
  • LabVIEW 스레딩 모델
  • 하이퍼스레딩 및 다중 프로세서 시스템용 프로그래밍
  • 보다 복잡한 LabVIEW 예제
  • 하이퍼스레딩 컴퓨터 성능 향상시켜주는 LabVIEW 7.1
  • 리눅스

 

LabVIEW와 하이퍼스레딩

하이퍼스레딩 지원 프로세서는 하나의 마이크로칩에 임베드된 다중 프로세서와 같은 기능을 합니다. 이 칩에서 레지스터 셋과 같은 일부 리소스는 중복되어 있으며 실행 유닛 및 캐시와 같은 리소스의 경우에는 공유되고 마이크로 작업을 저장하는 버퍼 등의 리소스는 파티션 되어 각 논리적 프로세서가 한 부분씩 차지하도록 되어 있습니다. 어플리케이션을 최적화하여 하이퍼스레딩을 지원하도록 하는 과정은 여러 개의 프로세서를 장착한 시스템을 목적으로 어플리케이션을 최적화하는 것과 유사하지만 조금 다른 부분이 있습니다.
예를 들어, 하이퍼스레딩 컴퓨터는 실행 유닛을 공유하지만 듀얼 프로세서 컴퓨터는 완전한 실행 유닛 2개를 갖추고 있는 셈입니다. 그러므로 부동소수점 실행 유닛에 제한 받는 어플리케이션이라면 실행 유닛을 공유하지 않아도 되는 다중 프로세서 컴퓨터에서 더 좋은 성능을 보이게 됩니다. 이 개념은 캐시 경쟁에서도 적용될 수 있는데 만약 두 개의 스레드가 캐시에 접근하려 할 경우 역시 풀사이즈 캐시가 프로세서마다 있는 다중 프로세서 컴퓨터에서의 성능이 더 좋습니다.

LabVIEW 실행 개요

LabVIEW는 이미 다중 프로세서를 지원하고 있습니다. 텍스트 기반 프로그래밍 언어의 경우 어플리케이션이 다중 스레딩을 지원하게 만들려면 스레드를 여러 개 만들고 이 스레드끼리 통신하도록 코드를 작성해야 합니다. 하지만 LabVIEW는 VI에서 다중 스레딩이 발생하면 이를 인식하여 다중 스레딩 통신을 처리해줍니다.

다음 예제는 LabVIEW 다중 스레드 실행 시스템을 활용한 것입니다.

 

 

이 VI에서 LabVIEW는 While 루프 2개를 독립적으로 실행할 수 있음을 인식하며 다중 프로세서 또는 하이퍼스레딩 환경에서는 이를 동시에 처리하기도 합니다.

 

소수 병렬수행 예제

다음 예제는 2보다 큰 소수를 계산하는 것입니다.

 

블록다이어그램은 3과 Num Terms 사이의 홀수의 값을 모두 구하여 소수인지 판단합니다. 나머지가 0이 되게 Term을 나눠주는 숫자가 있을 경우 내부의 For 루프는 TRUE 값을 돌려줍니다.
I/O가 포함되어 있지 않고 함수를 기다리기 때문에 내부 For 루프는 연산 집중적이 됩니다. 이 VI의 구조로 인해 LabVIEW는 병렬수행을 이용할 수 없습니다. 루프에 포함된 작업 전체에는 의무적 명령이 주어지고 이 명령은 데이터 흐름에 의해 강제되며 각 작업은 해당 입력을 대기하고 있어야 하므로 다른 실행 명령은 불가능합니다.
이 VI에 병렬수행을 삽입할 수 있습니다. 병렬수행을 하려면 루프 반복간에 의존해서는 안됩니다. 이 조건을 만족하게 되면 두 루프 간에 루프 반복을 분배할 수 있습니다. 하지만 LabVIEW의 제한사항 한 가지는 이전 반복이 완료되기 전에 다른 루프가 반복될 수 없다는 점입니다. 이 제한사항이 필요치 않다고 판단되면 프로세스를 루프 2개로 분리할 수 있습니다.
다음 그림에서는 소수 병렬수행 예제가 프로세스를 2개의 루프로 분리합니다. 위의 루프는 홀수 중 절반을 처리하고 아래 루프는 나머지 절반을 처리합니다. 다중 프로세서 컴퓨터라면 LabVIEW가 동시에 양쪽 루프에서 코드를 실행할 수 있으므로 2중 루프가 더 효율적이 됩니다. 이 VI의 결과가 예전과 달리 배열이 하나가 아닌 2개임에 주목하십시오. 이 배열들을 결합하는 SubVI를 작성할 수 있으며 계산 작업이 실행 시간을 대부분 사용하기 때문에 프로세스 끝부분의 추가 VI는 무시해도 좋습니다.

 

 

이 예제 VI를 보면 명시적 스레드 관리를 위한 코드가 없다는 점이 눈에 띕니다. LabVIEW 데이터 흐름 프로그래밍 패러다임은 LabVIEW로 하여금 For 루프 2개를 각각 다른 스레드에서 실행되도록 합니다. 텍스트 기반 프로그래밍 언어에서는 대개 명시적으로 스레드를 만들고 처리해야 합니다.

 

LabVIEW 스레딩 모델

LabVIEW는 버전 5.0부터 다중 스레딩을 지원하기 시작했습니다. 이 버전은 사용자 인터페이스 스레드와 연산 스레드를 분리시켰습니다. 블록다이어그램은 하나 또는 가능하면 그 이상의 스레드에서 실행되었으며 프런트 패널 업데이트는 다른 스레드에서 이루어졌습니다. 운영체제는 연산 스레드에 프로세서의 실행 시간을 허용하고 그 다음에 사용자 인터페이스 스레드에 할당하는 등의 방식으로 주도권을 잡고 여러 스레드를 멀티태스킹으로 만들었습니다.

7.0 이전의 LabVIEW에서는 기본값으로 단 하나의 스레드를 단일 우선순위 및 실행 시스템에 할당했는데 이로 인해 동일한 우선순위 및 실행 시스템에 속한 VI의 모든 활동이 함께 대기해야 했습니다. LabVIEW 7.0에서는 단일 우선순위 및 실행 시스템에 할당되는 스레드의 기본 값을 4개로 늘려 스레드 하나가 대기상태를 초래하는 명령어 하나를 실행할 경우에도 블록다이어그램의 다른 부분에서는 실행을 계속할 수 있게 되었습니다.

LabVIEW는 운영체제의 선점형 멀티태스킹 외에 협력형 멀티태스킹도 지원합니다. 컴파일 중에 LabVIEW는 VI를 분석하여 클럼프(Parallel pieces of code (parts of the code that are independent from each other) within a time-critical loop are referred to as clumps.)라 불리는 것 안에서 함께 실행될 수 있는 노드 그룹을 찾아냅니다. 각 우선순위 및 실행 시스템에는 어느 클럼프가 함께 실행될 수 있는지 정보를 담고 있는 실행 대기열 데이터 구조가 있습니다. 운영체제가 스레드를 활성화시킬 경우 실행 대기열로부터 클럼프를 받아서 연산합니다. 연산을 마치면 실행 대기열의 입력 조건을 만족시키는 클럼프를 추가로 저장하고 블록다이어그램은 이를 남아있는 연산 스레드 4개 중 하나에서 연산하게 됩니다. 블록다이어그램에 병렬수행이 충분히 포함되면 4개 스레드 전체를 동시에 연산할 수 있습니다.

LabVIEW는 코드 클럼프를 특정 스레드에 할당하지는 않으며 VI를 다시 실행하면 다른 스레드를 통해 클럼프를 연산합니다.

 

하이퍼스레딩 및 다중 프로세서 시스템용 프로그래밍

하이퍼스레딩 컴퓨터를 위해 어플리케이션의 성능을 최적화하는 것은 다중 프로세서 컴퓨터의 경우와 거의 동일합니다. 하지만 하이퍼스레딩 컴퓨터는 다중 프로세서 2개에서 캐시나 실행 유닛과 같은 리소스를 공유하기 때문에 차이는 분명히 존재합니다. 하이퍼스레딩 컴퓨터에서 공유된 리소스로 인해 어플리케이션이 제약을 받는다고 생각한다면 인텔 VTune과 같은 고급 샘플링 성능 분석기로 어플리케이션을 테스트해 보십시오.

다음 코드 예제는 앞 섹션에서 C++로 소수 병렬수행 예제를 수정하여 다중 스레드 프로그램을 텍스트 기반 프로그래밍 언어에서 작성하는 방법을 나타낸 것입니다. 이 예제는 스레드 처리 코드를 작성하는 것이 쉽지 않다는 점과 스레드가 공유하는 데이터를 보호하는 데 필요한 특수 코딩을 보여줍니다.

 

C++ 소수 병렬 연산 예제

다음 샘플 코드는 마이크로소프트 비주얼 C++ 6.0에서 작성 및 테스트한 것입니다. 본 문서 앞부분의 LabVIEW 예제에 있는 같은 알고리즘을 따르는 단일 스레드 소수 병렬 연산 예제로 다음과 같은 형태가 됩니다:

// Single-threaded version.
void __cdecl CalculatePrimes(int numTerms) {
bool *resultArray = new bool[numTerms/2];
for (int i=0; i<numTerms/2; ++i) {
int primeToTest = i*2+3; // Start with 3, then add 2 each iteration.
bool isPrime = true;
for (int j=2; j<=sqrt(primeToTest); ++j) {
if (primeToTest % j == 0) {
isPrime = false;
break;
}
}
resultArray[i] = isPrime;
}
ReportResultsCallback(numTerms, resultArray);
delete [] resultArray;
}

이 단일 스레드 기반 코드 예제에는 병렬 수행이 없으며 이로 인해 가상 프로세서 1개를 완전히 점유하게 됩니다. 다른 프로세서의 대역폭을 조금이라도 사용하려면 어플리케이션에서 추가 스레드를 가동하여 작업을 분배해야 합니다.

다음 코드는 다중 스레드를 쓰는 소수 병렬 연산 예제입니다.

struct ThreadInfo {
int numTerms;
bool done;
bool *resultArray;
};

static void __cdecl CalculatePrimesThread1(void*);
static void __cdecl CalculatePrimesThread2(void*);

void __cdecl CalculatePrimesMultiThreaded(int numTerms) {

// Initialize the information to pass to the threads.
ThreadInfo threadInfo1, threadInfo2;
threadInfo1.done = threadInfo2.done = false;
threadInfo1.numTerms = threadInfo2.numTerms = numTerms;
threadInfo1.resultArray = threadInfo2.resultArray = NULL;

// Start two threads
_beginthread(CalculatePrimesThread1, NULL, &threadInfo1);
_beginthread(CalculatePrimesThread2, NULL, &threadInfo2);

// Wait for the threads to finish executing.
while (!threadInfo1.done || !threadInfo2.done)
Sleep(5);

// Collate the results.
bool *resultArray = new bool[numTerms/2];
for (int i=0; i<numTerms/4; ++i) {
resultArray[2*i] = threadInfo1.resultArray[i];
resultArray[2*i+1] = threadInfo2.resultArray[i];
}
ReportResultsCallback(numTerms, resultArray);
delete [] resultArray;
}

static void __cdecl CalculatePrimesThread1(void *ptr) {
ThreadInfo* tiPtr = (ThreadInfo*)ptr;
tiPtr->resultArray = new bool[tiPtr->numTerms/4];
for (int i=0; i<tiPtr->numTerms/4; ++i) {
int primeToTest = (i+1)*4+1;
bool isPrime = true;
for (int j=2; j<=sqrt(primeToTest); ++j) {
if (primeToTest % j == 0) {
isPrime = false;
break;
}
}
tiPtr->resultArray[i] = isPrime;
}
tiPtr->done=true;
}

static void __cdecl CalculatePrimesThread2(void *ptr) {
ThreadInfo* tiPtr = (ThreadInfo*)ptr;
tiPtr->resultArray = new bool[tiPtr->numTerms/4];
for (int i=0; i<tiPtr->numTerms/4; ++i) {
int primeToTest = (i+1)*4+3;
bool isPrime = true;
for (int j=2; j<=sqrt(primeToTest); ++j) {
if (primeToTest % j == 0) {
isPrime = false;
break;
}
}
tiPtr->resultArray[i] = isPrime;
}
tiPtr->done=true;
}

본 예제에서 CalculatePrimesMultiThreaded() 함수는 _beginthread() 함수를 사용하는 스레드 2개를 만듭니다. 첫 번째 스레드는 CalculatePrimesMultiThreaded() 함수를 호출하여 홀수의 절반을 테스트하고 두 번째 스레드는 CalculatePrimesMultiThreaded2 함수를 호출하여 나머지 절반을 테스트합니다.

CalculatePrimesMultiThreaded() 함수를 계속 실행중인 원래 스레드는 작업중인 스레드 2개가 각 스레드에 대해 데이터 구조 ThreadInfo를 만들어 이를 _beginthread() 함수로 넘겨줄 때까지 기다려야 합니다. 또한 원래 스레드가 각 연산 스레드에 대해 진짜 값을 읽을 때까지 계속해서 ThreadInfo::done을 폴링해야 하며 그 후에는 단일 스레드를 사용한 예제의 경우와 똑같은 결과가 나오도록 이 값들을 단일 배열로 합칩니다. 주의: 방금 설명한 단계는 LabVIEW 예제에 포함되지 않았지만 쉽게 처리할 수 있는 작업입니다.

C++과 같은 텍스트 기반 프로그래밍 언어로 다중 스레딩 코드를 작성할 때는 다중 스레드가 접근할 수 있는 데이터 위치를 보호해야 합니다. 그렇지 않을 경우 나타나는 결과는 극심할 정도로 예측하기 어려우며 재처리와 위치 찾기까지 어려운 경우도 있습니다. tiPtr->done을 true로 맞추는 상황이 atomic(원자)가 아니라면 Mutex를 사용하여 할당 및 접근을 보호해야 합니다.

하지만 앞의 예제를 개선할 방법은 있습니다. 특히 스레드 2개를 추가로 가동하고 하나를 유휴 상태로 둘 이유는 없습니다. 본 예제에는 두 개의 가상 프로세서를 사용하는 컴퓨터를 대상으로 한 코드가 들어있으므로 스레드 하나만을 추가로 가동하고 원래 스레드로 연산의 절반을 수행할 수 있습니다. 또한 원래 같은 함수를 두 번 작성하는 대신 같은 함수를 통해 양쪽 스레드를 모두 우회할 수도 있습니다. 어느 스레드에서 실행될 수 있는지 나타내도록 추가 매개변수를 함수에 넘겨줄 필요가 있습니다. 주의: For 루프를 포함하는 재진입성 서브VI를 만듦으로써 LabVIEW 예제에도 동일한 최적화를 수행할 수 있습니다. 호출하는 VI는 For 루프 두 개 대신 서브VI 두 개를 실행하게 됩니다. 재진입성 VI에 대한 자세한 정보는 Application Note 114(아래 링크 참고) Using LabVIEW to Create Multithreaded VIs for Maximum Performance and Reliability에서 볼 수 있습니다.

다음 코드는 효율성이 보다 강화된 다중 스레딩 어플리케이션입니다:

struct ThreadInfo2 {
int threadNum;
int numTerms;
bool done;
bool *resultArray;
};

static void __cdecl CalculatePrimesThread(void*);

void __cdecl CalculatePrimesMultiThreadedSingleFunc(int numTerms) {

// Initialize the information to pass to the threads.
ThreadInfo2 threadInfo1, threadInfo2;
threadInfo1.done = threadInfo2.done = false;
threadInfo1.numTerms = threadInfo2.numTerms = numTerms;
threadInfo1.resultArray = threadInfo2.resultArray = NULL;
threadInfo1.threadNum = 1;
threadInfo2.threadNum = 2;

// Start a thread
_beginthread(CalculatePrimesThread, NULL, &threadInfo1);

// Use this thread for the other branch instead of spawning another thread.
CalculatePrimesThread(&threadInfo2);

// Maybe this thread finished first. If so, wait for the other.
while (!threadInfo1.done)
Sleep(5);

// Collate the results.
bool *resultArray = new bool[numTerms/2];
for (int i=0; i<numTerms/4; ++i) {
resultArray[2*i] = threadInfo1.resultArray[i];
resultArray[2*i+1] = threadInfo2.resultArray[i];
}
ReportResultsCallback(numTerms, resultArray);
delete [] resultArray;
}

static void __cdecl CalculatePrimesThread(void *ptr) {
ThreadInfo2* tiPtr = (ThreadInfo2*)ptr;
int offset = (tiPtr->threadNum==1) ? 1 : 3;
tiPtr->resultArray = new bool[tiPtr->numTerms/4];
for (int i=0; i<tiPtr->numTerms/4; ++i) {
int primeToTest = (i+1)*4+offset;
bool isPrime = true;
for (int j=2; j<=sqrt(primeToTest); ++j) {
if (primeToTest % j == 0) {
isPrime = false;
break;
}
}
tiPtr->resultArray[i] = isPrime;
}
tiPtr->done=true;
}

보다 적은 수의 스레드를 가동하고 작업 스레드가 완료할 때까지 기다리는 시간도 더 짧으므로 위 예제가 보다 효율적이라 할 수 있습니다. 게다가 단일 함수 내에서 연산을 수행하기 때문에 중복 코드도 줄어들어 어플리케이션을 유지하기가 용이해집니다. 단 코드에서 많은 부분이 스레드 관리에 할당되며 이는 원래 테스트 및 디버그가 불안정하고 어려운 부분입니다.

 

보다 복잡한 LabVIEW 예제

소수 병렬수행 예제는 다중 프로세싱에 포함된 많은 개념들을 보여주는 간단한 예제입니다. 하지만 실제로 사용되는 어플리케이션은 그렇게 간단하지 않습니다. 소수 생성기를 작성해야 할 때도 본 문서의 예제에서 사용된 알고리즘은 그다지 효율적이지 못합니다.

다음 섹션은 LabVIEW 다중 스레드 실행 시스템에서 도움이 되는 연산 집중적 알고리즘 한가지를 더 보여줍니다. 아래 그림의 블록다이어그램은 사용자가 지정하는 자리 수대로 파이 값을 계산하는 과정입니다.

 

 

 

분홍색 와이어 경로는 임의 정밀 수치 값 역할을 하는 클러스터입니다. 파이를 1,000 자리까지 연산하고자 한다면 확장 정밀 부동 소수 값 이상의 것이 있어야 합니다. LabVIEW 함수처럼 보이지만 분홍 클러스터에서 작동하는 업들은 임의 정밀수에 대해 연산하는 VI들입니다.
또한 이 VI는 연산 집중적으로 다음 공식을 바탕으로 파이를 연산합니다.

 

 

 

이 방정식의 계산적 복잡성을 감안해보면 대부분의 텍스트 기반 프로그래밍 언어에서 하이퍼스레딩 컴퓨터의 논리적 프로세서 2개를 모두 사용하는 프로그램을 작성하기란 쉽지 않을 것입니다. 그러나 LabVIEW는 위의 방정식을 분석하여 다중 프로세싱을 할 수 있는 기회로 인식합니다.

LabVIEW가 다음 블록다이어그램을 분석할 때는 내재된 병렬수행을 찾아냅니다. 빨간색 및 녹색으로 강조된 섹션들 사이에 데이터 흐름 의존성이 나타나지 않는다는 점에 주목하십시오.

 

 

 

이 블록다이어그램은 언뜻 보기엔 병렬수행이 거의 없는 것처럼 보입니다. 단 두 작업만이 나머지 VI와 병렬로 수행할 수 있는 듯 하며 이 두 작업은 단 한번만 실행됩니다. 사실 제곱근은 시간을 소모하는 작업이며 이 VI는 전체 수행 시간 중 약 절반을 For 루프와 병렬로 제곱근 작업을 처리하는데 소비합니다. VI를 서로 무관한 두 개의 루프로 나누면 LabVIEW는 병렬수행을 인식하고 논리적 프로세서 두 개를 모두 사용해서 스레드들을 병렬로 처리하며 그 결과로 성능이 대폭 향상됩니다. 이러한 방법은 다중 프로세서 컴퓨터에서와 유사한 방식으로 하이퍼스레딩 컴퓨터에서도 효력을 발휘합니다.

병렬수행을 찾아내기 위해 어플리케이션을 분석하기란 어렵습니다. C++와 같은 텍스트 기반 프로그래밍 언어를 사용한다면 병렬수행이 존재할 가능성을 알아차리고 하이퍼스레딩 기능을 활용할 스레드를 명시적으로 만들고 실행해야 합니다. LabVIEW를 사용함으로써 얻는 장점은 이와 비슷한 많은 경우에 실행 시스템이 논리적 프로세서 두 개를 모두 사용할 수 있도록 LabVIEW가 자동으로 병렬수행을 찾아낸다는 사실입니다. 일부 어플리케이션에서는 LabVIEW의 다중 스레딩 기능을 활용할 수 있게끔 병렬수행을 사용할 기회를 만들어내는 알고리즘을 쓰는 쪽이 도움이 되기도 합니다.

빨간색 및 녹색으로 강조된 작업 외에도 이 블록다이어그램에는 For 루프에 더 많은 병렬연산을 찾아볼 수 있습니다. LabVIEW는 이 작업들을 병렬로 수행하여 하이퍼스레딩 컴퓨터에서 시간을 절약해줍니다. 하지만 데이터 스트림의 후반부에 데이터 흐름 의존성이 나타나므로 LabVIEW가 강조된 부분을 분리함으로써 더 많은 효과를 얻을 수 있습니다.

아래는 파이를 1,500 자리까지 계산할 때 성능 최적화를 하지 않은 VI가 소모하는 시간을 나타낸 것입니다:
• 하이퍼스레딩 - 27.9 s
• 비 하이퍼스레딩 - 25.3 s

하이퍼스레딩 컴퓨터의 수행 속도가 더 느리다는 점에 주목하십시오. 하이퍼스레딩 컴퓨터의 논리적 프로세서들은 캐시를 공유하기 때문에 캐시 라인이 압도당해 스래싱이 발생할 확률이 높습니다. LabVIEW는 전체 수행 스레드가 읽고 쓸 수 있어야 하는 메모리의 한 영역에다 디버깅에 필요한 정보를 저장합니다. 스레드 두 개가 각기 다른 논리적 프로세서에서 동시에 실행하고 한 스레드가 디버그 정보에 쓰는 동시에 다른 스레드는 읽어들이는 상황도 종종 있습니다. 두 개의 스레드가 하이퍼스레딩 컴퓨터의 같은 캐시 라인에서 바이트 단위로 작업을 수행할 경우 상당한 성능 저하가 발생할 수 있습니다.

디버깅 기능을 억제해서 VI의 성능을 향상시키는 방법도 있습니다. VI의 성능에 결정적인 영향을 미치는 부분에 디버깅을 억제시키고 이 부분을 디버그 할 필요가 생기면 다시 활성화시킬 수 있습니다.

다음 표는 VI 전체적으로 디버깅을 억제한 후 파이를 1,500 자리까지 계산하는 데 걸린 시간입니다:
• 하이퍼스레딩 - 21.6 s
• 비 하이퍼스레딩 - 21.5 s

하이퍼스레딩 컴퓨터와 비 하이퍼스레딩 컴퓨터간의 성능차이가 거의 없음을 주목하십시오. LabVIEW가 병렬수행을 해도 작업을 분할함으로써 걸리는 부하가 성능을 향상시켜주지는 못하는 듯 합니다.

다음은 LabVIEW 7.1에서 소수 병렬수행을 실행한 결과입니다. 3.06 GHz의 펜티엄 4에 Windows XP를 사용하는 컴퓨터에서 VI가 처음 400,000개의 자연수 중 소수를 모두 찾아내는 작업을 수행했습니다.
• 디버깅 하지 않은 하이퍼스레딩 - 1.97 초
• 디버깅 한 하이퍼스레딩 - 10.47 초

디버깅을 한 경우에 생기는 차이는 상당히 크지만 두 병렬 영역에서 같은 VI를 수행할 경우에만 이런 차이가 생깁니다. 여러 개의 VI가 있는 어플리케이션이거나 VI가 디버깅 정보를 여러 캐시 라인에서 분리해서 대부분 처리할 수 있을 정도로 복잡하다면 디버깅으로 인한 차이는 이 정도로 심하지 않을 것입니다.

하이퍼스레딩 컴퓨터의 성능을 최적화시키는 다른 방법으로는 서브VI 가운데 일부를 재진입성으로 만드는 것입니다. 서브VI가 재진입성이 되면 여러 스레드가 같은 서브VI에 호출할 수 있게 됩니다. 다중 프로세서나 하이퍼스레딩 컴퓨터를 대상으로 VI를 최적화할 때는 LabVIEW 수행 시스템의 재진입성 수행을 이해하는 것이 중요합니다. 재진입성으로 만드는 과정에 오류가 있을 경우 특히 다중 프로세서 환경에서 불필요한 지연현상이 발생할 수 있습니다. 재진입성 VI에 대한 보다 자세한 정보는 Application Note (아래 링크) Using LabVIEW to Create Multithreaded VIs for Maximum Performance and Reliability를 참고하십시오.

어느 서브VI가 재진입성이 될 수 있는지 정하려면 병렬수행 실행 호출의 양쪽 갈림을 찾아야 합니다. Browse»Show VI Hierarchy 메뉴를 통해 메인 VI의 계층 윈도우를 띄워 양쪽 실행 호출 갈림이 사용하는 함수와 VI를 찾을 수 있습니다.
앞의 예제에서는 Square Root 함수와 For 루프 병렬로 작동합니다.

 

 

 

최상위 VI는 Square Root, Add, Multiply 함수와 For 루프에 있는 Multiply Scalar와 Divide Scalar VI를 호출합니다. LabVIEW가 각기 다른 스레드에서 이 영역을 호출하기 때문에 재진입성으로 만드는 데 최적인 영역은 두 군데만이 있습니다. 다음 표는 Add와 Multiply 함수 및 Multiply Scalar와 Divide Scalar VI를 재진입성으로 마크한 VI가 파이를 1,500 자리까지 계산하는 데 걸린 시간입니다:
• 하이퍼스레딩 - 20.5 s
• 비 하이퍼스레딩 - 21.9 s

VI를 비진입성으로 마크하면 하이퍼스레딩 컴퓨터에서의 VI 수행 속도가 다소 빨라집니다. VI 4개를 비진입성으로 만든 후의 비 하이퍼스레딩 컴퓨터에서 수행 시간이 약간 느린데 그 이유는 LabVIEW가 비진입성 VI를 호출할 때 데이터스페이스를 추가로 만들기 때문입니다.
VI Profiler를 사용해서 어플리케이션을 더욱 최적화시키고 서브루틴 우선순위에서 일부 VI가 실행되도록 지명할 수 있습니다. 또한 더 많은 VI를 재진입성으로 마크할 수도 있습니다. 다음 표는 최적화를 여러 번 수행한 VI가 파이를 1,500 자리까지 계산하는 데 걸린 시간입니다:
• 하이퍼스레딩 - 18.0 s
• 비 하이퍼스레딩 - 20.4 s

이 결과를 보면 하이퍼스레딩 컴퓨터가 약 10% 앞서는 성능을 나타내고 있습니다. 이 성능 향상을 얻기 위해 VI 코드를 변경하는 수고를 들일 필요는 없었다는 점에 주목하십시오. 디버깅을 억제하고 VI 일부를 재진입성으로 만들며 일부 VI는 서브루틴 우선순위에서 실행되도록 한 것이 전부입니다. 이러한 변경을 통해 하이퍼스레딩 컴퓨터에서 얻은 성능 향상은 약 35%가 됩니다.

 

하이퍼스레딩 컴퓨터 성능을 향상시켜주는 LabVIEW

LabVIEW 7.1 이후 버전부터는 하이퍼스레딩 컴퓨터의 논리적 프로세서 2개 간에 캐시를 공유하도록 최적화되었습니다. 하이퍼스레딩 기술이 처음 발표되었을 때 LabVIEW를 포함한 다중 스레드 어플리케이션은 성능을 향상시킬 가능성을 가진 이 신기술을 위해 수정을 거쳐야 했습니다. 하이퍼스레딩 이전에 개발된 어플리케이션의 경우 특히 스레드 2개가 하나의 캐시를 공유해야 하는 경우에 하이퍼스레딩을 지원하는 신형 프로세서에서 성능이 더 낮아지는 상황도 있었습니다. 결과적으로 LabVIEW 7.0과 이전 버전은 하이퍼스레딩 컴퓨터에서 더 속도가 느린 경우가 많습니다. 7.1 버전으로 업그레이드할 상황이 아니지만 하이퍼스레딩 컴퓨터에서 어플리케이션의 속도를 향상시켜야 하는 경우에는 하이퍼스레딩 기능을 끄거나 프로세스의 CPU 친화성을 단일 논리적 CPU로 맞추십시오.
다음 표는 소수 병렬수행 예제를 실행한 결과를 초 단위로 나타낸 것입니다. 3.06 GHz의 펜티엄 4 CPU에 Windows XP를 구동하는 컴퓨터에서 처음 400,000개의 자연수 가운데 소수를 찾아내는 작업입니다.

 

         버전

   하이퍼스레딩

  비 하이퍼스레딩

  LabVIEW 7.0         6.77          3.39
  LabVIEW 7.1         1.91          3.40

 

하이퍼스레딩을 지원하지 않는 경우에 나타난 LabVIEW 7.0과 7.1 간의 성능 차이는 샘플링 오류로 인한 것입니다.

 

리눅스

앞에서 본 성능 벤치마크는 모두 Windows 운영체제를 사용하는 컴퓨터에서 진행되었습니다. 그러나 리눅스도 하이퍼스레딩 컴퓨터에서 구동이 가능합니다. 리눅스는 다양한 변종이 많기 때문에 그 변종마다 정확한 LabVIEW 벤치 마킹 결과를 얻기가 어렵습니다. 하지만 테스트 결과에 따르면 하이퍼스레딩을 지원하는 다중 프로세서 리눅스 시스템에서 LabVIEW 7.1이 이전 버전보다 빠른 성능을 보여주었습니다.

관련 링크:

LabVIEW Development System