루프 레벨 병렬 처리
Loop-level parallelism루프 레벨 병렬화는 소프트웨어 프로그래밍에서 루프에서 병렬 태스크를 추출하는 것과 관련된 병렬 처리의 한 형태입니다.루프 레벨 병렬화의 기회는 데이터가 랜덤 액세스 데이터 구조에 저장되는 컴퓨팅 프로그램에서 종종 발생합니다.순차적 프로그램이 데이터 구조에서 반복되어 한 번에 하나씩 인덱스에서 동작하는 경우 루프 레벨 병렬화를 이용하는 프로그램은 동시에 일부 또는 모든 인덱스에서 동작하는 여러 스레드 또는 프로세스를 사용합니다.이러한 병렬화는 일반적으로 Amdahl의 법칙에 따라 프로그램의 전체 실행 시간을 단축합니다.
묘사
각 반복이 다른 반복과 독립되어 있는 단순 루프의 경우 각 반복을 처리하는 프로세스만 할당하면 되므로 루프 레벨 병렬화는 당혹스러울 정도로 병렬화될 수 있습니다.그러나 많은 알고리즘이 순차적으로 실행되도록 설계되어 있으며 코드 내 의존성으로 인해 병렬 프로세스가 경주할 때 실패합니다.시퀀셜 알고리즘은 약간의 변경을 수반하는 병행 컨텍스트에 적용할 수 있습니다.다만, 통상은 프로세스의 동기화가 필요합니다.동기화는 메시지 전달을 통해 암묵적으로 수행되거나 세마포어 등의 동기화 프리미티브를 통해 수행될 수 있습니다.
예
리스트에서 동작하고 있는 다음의 코드를 생각할 수 있습니다.L
길쭉한n
.
위해서 (인트 i = 0; i < > n; ++i) { S1: L[i] += 10; }
루프의 각 반복은 현재 인덱스에서 값을 가져옵니다.L
10씩 증가합니다.If 스테이트먼트S1
걸린다T
루프가 실행되는 데 시간이 걸립니다.n * T
루프 구조에 걸리는 시간을 무시하고 순차적으로 실행합니다.이제 다음 시스템을 고려해 보겠습니다.p
프로세서의 위치p > n
.한다면n
병렬로 실행되는 스레드, 모든 실행 시간n
스텝은 로 단축됩니다.T
.
단순하지 않은 경우는 일관성이 없는 결과, 즉 연속할 수 없는 결과를 낳는다.다음 루프가 같은 리스트에서 동작하고 있다고 생각해 주세요.L
.
위해서 (인트 i = 1; i < > n; ++i) { S1: L[i] = L[i-1] + 10; }
각 반복은 현재 인덱스를 이전 값과 10의 값으로 설정합니다.순차적으로 실행할 경우 각 반복은 이전 반복의 값이 이미 올바른지 확인합니다.여러 스레드에서는 프로세스 스케줄링 및 기타 고려사항으로 인해 종속성이 충족된 후에만 반복이 실행되도록 보장할 수 없습니다.그것은 이전에 매우 잘 일어날 수 있고, 예상치 못한 결과로 이어질 수도 있습니다.이전 반복에 대한 의존성을 유지하기 위해 동기화를 추가하여 직렬성을 복원할 수 있습니다.
코드의 종속성
코드 [1][2]내에서 찾을 수 있는 의존관계에는 몇 가지 유형이 있습니다.
유형 | 표기법 | 묘사 |
---|---|---|
참(흐름) 의존성 | S1 ->T S2 | S1과 S2의 진정한 의존성은 S1이 S2에 의해 나중에 읽혀진 위치에 쓰임을 의미합니다. |
안티의존성 | S1 ->A S2 | S1과 S2의 반의존성은 S1이 나중에 S2에 의해 기입된 위치에서 읽는 것을 의미한다. |
출력 의존성 | S1 ->O S2 | S1과 S2의 출력 의존성은 S1과 S2가 같은 위치에 쓰임을 의미한다. |
입력 의존성 | S1 ->I S2 | S1과 S2의 입력 의존성은 S1과 S2가 같은 위치에서 읽혔음을 의미한다. |
병렬 실행 시 루프의 순차적 동작을 유지하려면 True Dependence를 유지해야 합니다.안티의존성 및 출력의존성은 각 프로세스에 고유한 변수 복사본을 제공하여 처리할 수 있습니다(민영화라고 [1]함).
진정한 의존성의 예
S1: 인트 a, b; S2: a = 2; S3: b = a + 40;
S2 ->T S3
즉, S2가 변수에 쓰기 때문에 S2는 S3에 진정으로 의존합니다.a
S3에서 읽습니다.
안티의존성의 예
S1: 인트 a, b = 40; S2: a = b - 38; S3: b = -1;
S2 ->A S3
즉, S2는 변수에서 읽기 때문에 S2는 S3에 대한 반의존성이 있습니다.b
S3가 써넣기 전에.
출력 의존성의 예
S1: 인트 a, b = 40; S2: a = b - 38; S3: a = 2;
S2 ->O S3
즉, 양쪽 모두 변수에 쓰기 때문에 S2는 S3에 의존합니다.a
.
입력 의존성의 예
S1: 인트 a, b, c = 2; S2: a = c - 1; S3: b = c + 1;
S2 ->I S3
즉, S2와 S3는 모두 변수에서 읽기 때문에 S2는 S3에 대한 입력 의존성이 있습니다.c
.
루프의 의존성
루프 캐리어와 루프 비의존 의존성
루프에는 다음 2종류의 의존관계가 있습니다.
- 루프 캐리어 의존성
- 루프에 의존하지 않는 의존성
루프에 의존하지 않는 경우 루프는 반복 간 의존성을 가지지만 반복 간 의존성은 없습니다.각 반복은 블록으로 처리되어 다른 동기화 작업 없이 병렬로 수행될 수 있습니다.
다음 코드 예에서는 길이n의 2배열 값을 스왑하기 위해 사용됩니다.루프에 의존하지 않는 의존하지 않습니다.S1 ->T S3
.
위해서 (인트 i = 1; i < > n; ++i) { S1: tmp = a[i]; S2: a[i] = b[i]; S3: b[i] = tmp; }
루프 캐리어 의존관계에서는 루프의 반복에 있는 문은 루프의 다른 반복에 있는 문장에 의존합니다.루프 캐리어 의존관계는 앞에서 설명한 의존관계 표기법의 수정 버전을 사용합니다.
루프 캐리어 의존성의 예:S1[i] ->T S1[i + 1]
,어디에i
현재 반복을 나타냅니다.i + 1
에 다음 반복을 나타냅니다.
위해서 (인트 i = 1; i < > n; ++i) { S1: a[i] = a[i-1] + 1; }
루프 반송 종속성 그래프
루프 캐리어 의존관계는 반복간의 루프 캐리어 의존관계를 그래프로 나타냅니다.각 반복은 그래프에 노드로 나열되며 방향 에지는 각 반복 간의 참, 반 및 출력 종속성을 나타냅니다.
종류들
루프를 병렬화하는 방법에는 여러 가지가 있습니다.
- 분산 루프
- DOALL 병렬 처리
- DOACROSS 병렬 처리
- 헬릭스
- DOPIPE 병렬 처리
각 실장은 스레드 동기화 방법에 따라 조금씩 다릅니다.또한 병렬 태스크는 어떻게든 프로세스에 매핑되어야 합니다.이러한 작업은 정적 또는 동적으로 할당할 수 있습니다.연구에 따르면 로드밸런싱은 [4]정적 할당 알고리즘보다 일부 동적 할당 알고리즘을 통해 더 잘 수행될 수 있습니다.
시퀀셜 프로그램을 병렬화하는 프로세스는 다음과 같은 개별 [1]단계로 나눌 수 있습니다.아래의 각 구체적인 루프 병렬화는 암묵적으로 실행됩니다.
유형 | 묘사 |
---|---|
분해 | 프로그램은 태스크로 분류되며, 이는 이용 가능한 최소 동시성 단위입니다. |
과제 | 태스크는 프로세스에 할당됩니다. |
오케스트레이션 | 프로세스의 데이터 액세스, 통신 및 동기화. |
매핑 | 프로세스는 프로세서에 바인드됩니다. |
분산 루프
루프가 루프 캐리어 의존관계에 있는 경우 루프를 여러 개의 다른 루프로 분산시키는 방법이 있습니다.이러한 분산 루프를 병렬로 실행할 수 있도록 서로 의존하지 않는 문은 구분됩니다.예를 들어, 다음 코드를 고려합니다.
위해서 (인트 i = 1; i < > n; ++i) { S1: a[i] = a[i-1] + b[i]; S2: c[i] += d[i]; }
루프에 루프 전송 종속성이 있습니다.S1[i] ->T S1[i+1]
단, S2와 S1은 루프에 의존하지 않기 때문에 다음과 같이 코드를 고쳐 쓸 수 있습니다.
루프 1: 위해서 (인트 i = 1; i < > n; ++i) { S1: a[i] = a[i-1] + b[i]; } 루프 2: 위해서 (인트 i = 1; i < > n; ++i) { S2: c[i] += d[i]; }
이제 loop1과 loop2를 동시에 실행할 수 있습니다.데이터 레벨의 병렬화에서와 같이 단일 명령을 다른 데이터에 대해 병렬로 수행하는 대신, 여기서는 다른 루프가 다른 데이터에 대해 다른 작업을 수행합니다.S1 및 S2의 실행시간을 1({ 및 S({2로 , 위의 코드 시퀀셜 형식의 실행시간을 n 1 + 로 .{ }2개의 다른 루프에서 nof 1 + n의 실행시간을 얻을 수 있습니다.이 타입의 병렬은 함수 또는 태스크 병렬이라고 부릅니다.
DOALL 병행
DOALL 병렬은 루프 내의 문이 독립적으로 실행될 수 있는 경우(루프 캐리어 의존 관계가 없는 경우)[1] 존재합니다.예를 들어 다음 코드가 어레이에서 읽히지 않습니다.a
어레이는 갱신하지 않습니다.b, c
어떤 반복도 다른 반복에 의존하지 않습니다.
위해서 (인트 i = 0; i < > n; ++i) { S1: a[i] = b[i] + c[i]; }
예를 들어 S1의 1회 을 T S1({1})이라고 하면 위의 코드 시퀀셜 형식의 실행시간은 n 1({ n이라고 합니다.모든 반복이 독립된 경우 DOALL 병렬이 존재하므로 모든 실행속도를 높일 수 있습니다.ich는 T 의 실행 시간을 제공합니다.이것은 순차 실행에서 1회 반복에 걸리는 시간입니다.
다음 예제에서는 단순화된 의사 코드를 사용하여 루프를 병렬화하여 각 반복을 독립적으로 실행하는 방법을 보여 줍니다.
begin_intersism(); 위해서 (인트 i = 0; i < > n; ++i) { S1: a[i] = b[i] + c[i]; 종말론(); } 블록();
DOACROSS 병렬 처리
DOACROSS Parallelism은 독립적으로 실행할 수 있는 계산을 추출하여 동시에 [5]실행함으로써 루프의 반복이 병렬화되는 곳에 존재합니다.
동기는 루프 캐리어 의존성을 적용하기 위해 존재합니다.
종속성이 있는 다음 동기 루프를 고려합니다.S1[i] ->T S1[i+1]
.
위해서 (인트 i = 1; i < > n; ++i) { a[i] = a[i-1] + b[i] + 1; }
각 루프 반복은 2개의 액션을 수행합니다.
- 계산한다.
a[i-1] + b[i] + 1
- 값 할당 대상
a[i]
값 계산a[i-1] + b[i] + 1
그 후 할당을 실행하면 다음 두 줄(스테이트먼트 S1과 S2)로 분해할 수 있습니다.
S1: 인트 tmp = b[i] + 1; S2: a[i] = a[i-1] + tmp;
첫 번째 줄,int tmp = b[i] + 1;
에는 루프 패스의 의존성이 없습니다.다음으로 루프는 병렬로 온도값을 계산한 후 할당을 동기화하여 병렬화할 수 있습니다.a[i]
.
포스트.(0); 위해서 (인트 i = 1; i < > n; ++i) { S1: 인트 tmp = b[i] + 1; 잠깐만요.(i-1); S2: a[i] = a[i-1] + tmp; 포스트.(i); }
S1 및 S2의 실행시간을 1({ 및 S({2로 , 위의 코드 시퀀셜 형식의 실행시간을 n 1 + 로 .{ } S 1 + S({의 시간을 제공하는 파이프라인 방식으로 반복 실행함으로써 달성됩니다.
DOPIPE 병렬화
DOPIPE Parallelism은 루프 반복이 동기화된 여러 [1]루프로 분산되는 루프 캐리어 종속성을 위한 파이프라인 병렬 처리를 구현합니다.DOPIPE의 목표는 조립 라인처럼 작동하는 것이며, 이전 [6]단계에서 사용할 수 있는 충분한 데이터가 확보되는 즉시 한 단계가 시작됩니다.
다음과 같은 종속성 동기 코드를 고려하십시오.S1[i] ->T S1[i+1]
.
위해서 (인트 i = 1; i < > n; ++i) { S1: a[i] = a[i-1] + b[i]; S2: c[i] += a[i]; }
S1은 순차적으로 실행해야 하지만 S2는 루프 캐리어 의존성이 없습니다.S1에 필요한 모든 계산을 직렬로 수행한 후 DOALL Parallelism을 이용하여 S2를 병렬로 실행할 수 있다.단, 이렇게 하면 속도 상승이 제한됩니다.보다 좋은 방법은 S1이 종료되었을 때 각 S1에 대응하는 S2가 실행되도록 병렬화하는 것이다.
파이프라인 병렬화를 구현하면 다음과 같은 루프 세트가 생성됩니다.여기서 첫 번째 루프가 대응하는 인덱스를 종료하는 즉시 인덱스에 대해 두 번째 루프가 실행될 수 있습니다.
위해서 (인트 i = 1; i < > n; ++i) { S1: a[i] = a[i-1] + b[i]; 포스트.(i); } 위해서 (인트 i = 1; i < > n; i++) { 잠깐만요.(i); S2: c[i] += a[i]; }
S1 및 S2의 실행시간을 1({ 및 S({2로 , 위의 코드 시퀀셜 형식의 실행시간을 n 1 + 로 .{ } fashion T S 1+( / S \ n} + (의 얻을 수 있는 파이프라인 방식으로 반복 실행함으로써 실현됩니다. 여기서 p는 병렬 프로세서의 수입니다.
「 」를 참조해 주세요.
레퍼런스
- ^ a b c d e Solihin, Yan (2016). Fundamentals of Parallel Architecture. Boca Raton, FL: CRC Press. ISBN 978-1-4822-1118-4.
- ^ Goff, Gina (1991). "Practical dependence testing". Proceedings of the ACM SIGPLAN 1991 conference on Programming language design and implementation - PLDI '91. pp. 15–29. doi:10.1145/113445.113448. ISBN 0897914287. S2CID 2357293.
- ^ Murphy, Niall. "Discovering and exploiting parallelism in DOACROSS loops" (PDF). University of Cambridge. Retrieved 10 September 2016.
- ^ Kavi, Krishna. "Parallelization of DOALL and DOACROSS Loops-a Survey".
{{cite journal}}
: 인용저널 필요(도움말), (도움말)외부 링크 - ^ Unnikrishnan, Priya (2012), "A Practical Approach to DOACROSS Parallelization", Euro-Par 2012 Parallel Processing, Lecture Notes in Computer Science, vol. 7484, pp. 219–231, doi:10.1007/978-3-642-32820-6_23, ISBN 978-3-642-32819-0, S2CID 18571258
- ^ "DoPipe: An Effective Approach to Parallelize Simulation" (PDF). Intel. Retrieved 13 September 2016.