|
프로파일 피드백을 이용하면 좀 더 좋은 성능의 CPU 집중적인 대용량 애플리케이션을 구축할 수 있습니다. 프로파일 피드백 최적화를 위해서는 애플리케이션을 두번 구축해야 하는 데 한번은 프로파일 데이타를 모으기 위해서이고 다른 한번은 프로파일을 이용해서 최적화된 코드를 생성하기 위해서 입니다. 이러한 요구사항은 많은 소프트웨어 벤더들이 그들의 애플리케이션을 프로파일 피드백을 이용해 구축 하는데 어려움을 줄 수 있으며, 또한 각 소프트웨어의 프로파일을 생성하는 것은 비실용적일 수 있습니다. 그러나 프로파일 피드백은 기존의 프로파일을 활용하여 프로파일 피드백 builds의 오버헤드를 최소화하면서도, 최적화를 이끌어내는 피드백의 이점을 그대로 유지할 수 있도록 합니다.
이 글은 프로파일 피드백의 모든 것을 예제와 함께 소개 합니다. 그리고 프로파일 피드백을 이용하여 구축하는 몇몇 팁들도 같이 제공합니다.
0. 소개
프로세서 스톨(stall) 현상은 수천만개의 명령어로 이루어진 대용량 애플리케이션에서 발생할 수 있는 문제 중 하나 입니다. 프로세서가 모든 명령어들을 칩에 모두 가지고 있을 수 없기 때문에 몇몇 명령어들은 메모리에서 불러올때까지 기다려야 합니다. 그러므로 개발자들은 고 수준의 명령어들을 적절하게 배치해서 프로세서의 스톨 현상을 줄이는 것이 중요합니다. 보통 개발자들은 엔드 유저가 아니므로 애플리케이션의 런타임 데이타를 수집하고 hot 코드(애플리케이션의 대부분의 시간을 소비하는 부분)를 확인하고 코드의 몇몇 블럭들을 재작성하고 재배치 하여 런타임의 성능을 향상시키는 것이 매우 성가신 작업입니다. 프로그래머들은 이러한 작업들을 썬 스튜디오 컴파일러가 지원하는 프로파일 기반 최적화 테크닉을 통해 해결할 수 있습니다. 코드의 런타임시의 동작이 프로파일의 형식으로 사용이 가능해지면 컴파일러에 의해 오브젝트 코드가 배치될 수 있으며 온칩 캐시 (Level-1 혹은 L1) 와 메모리가 런타임시에 효율적으로 사용 되도록 할수 있습니다.
썬 스튜디오 C, C++ 와 Fortran 컴파일러는 모두 프로파일 피드백 데이타를 이용해 최적의 코드를 생성할 수 있습니다. 비록 이글의 예제들은 모두 C 로 쓰여졌지만, 실행 방법은 어떠한 고수준의 언어로 개발되더라도 모든 어플레케이션에서 동일합니다. 또한 이 글에서 소개된 단계들은 글의 제목과 같이, 꼭 엔터프라이즈 애플리케이션 뿐만 아니라 어떠한 종류의 애플리케이션을 구축 하는데 있어서 역시 사용 가능합니다.
1. 피드백 기반 최적화
-O4) 혹은 그 이상에서는 자동으로 인라인화되지 않을 수 있습니다. 이러한 경우 프로파일 피드백 데이타를 사용 하는 것이 hot 코드를 인라인화 하는데 도움이 될 수 있습니다.피드백 기반 최적 (FBO) 는 런타임시에 수집된 정보를 이용해 프로그램을 조정하는 테크닉을 뜻합니다. 이 단어는 또한 피드백에 의해 지시된 최적화 (FDO) 그리고 프로파일 피드백 최적화(PFO)라고 광범위 하게 불리기도 합니다. 이 테크닉의 기본 아이디어는 프로그램의 런타임시의 행동들을 컴파일러에게 알려주는 것입니다. 컴파일러가 오브젝트를 설치함으로써 프로그램은 프로파일 되고, 이러한 프로파일 데이타가 컴파일러에 의해 사용되여 좀 더 빠르게 동작 할 수 있는 최적화 코드를 생성하는데 사용 됩니다.
프로파일 데이타가 사용 가능하면 컴파일러는 초기에 프로파일 피드백 파일의 각 블럭의 실행 횟수를 읽고, 이것을 프로그램의 intermediate representation (IR) 에 추가시킵니다. 이것은 모든 종류의 최적화의 초기 부분에 수행됩니다. 많은 컴파일러 최적화는 IR의 실행 횟수를 자주 사용 합니다. 프로파일 데이타에 기반을 두고 컴파일러는 다음과 같은 종류의 최적화를 수행합니다:
-
코드 레이아웃: 루틴에서 자주 수행되는 코드들을 한 그룹으로 묶는다. 이것은 명령어 캐시(I$) 미스를 줄이고 메모리상의 코드 레이아웃에 대한 정보를 프로파일 정보를 이용하여 명령어 페치(fetch) 속도를 높여 준다. 코드 레이아웃을 변경하여 애플리케이션의 퍼포먼스를 향상시키는 방법 은 프로파일 피드백을 이용해서 코드를 재배치 하는 것에 대해 설명한다.
-
인라이닝: 자주 불려지는 루틴들을 인라인화 한다. 자주 실행되지 않는 함수들은 비록 오토 인라이닝 조건을 만족하더라도 인라인 되지 않을 것이다. 인라이닝은 루틴을 호출할때 드는 비용을 제거해 준다; 그리고 최적화의 좀 더 좋은 기회를 제공한다.
-
레지스터 할당: 레지스터를 할당하기 위해 프로파일 데이타의 블럭 카운트를 활용한다.
-
루프 변환: Loop invariant code motion, loop fusion, loop interchange, loop peeling 등등.
-
분기 최적화: Tail splitting, branch interchange, loop unswitching 등등.
-
블럭 직렬화 혹은 아웃라이닝 : 자주 실행되지 않는 블럭들을 분리된 섹션으로 이동.
-
Switch case 코드 생성: 분기를 회피하기 위해 가장 많이 테스트 된 케이스.
-
글로벌 명령 스케줄링: 파이프라인에서 프로세서의 스톨을 막기 위한 의존성 없는 명령어들의 그룹짓기.
-
지연 슬롯 스케줄링: SPARC 과 같은 프로세서들의 분기/호출 지연 슬롯에서, 하나의 명령어 뒤에 위치한 명령어들이 마치 앞에 위치한 것처럼 실행된다면, 그 명령어는 슬롯지연이 필요하다고 함. 프로파일 데이타를 통해 스케줄러는 일반적인 분기를 위해 자주 실행되는 블럭들의 명령어를 사용함으로써 분기의 약점을 감소시킬 수 있음.
-
sethi감아 올리기:sethi명령어들을 감아 올려서 레지스터의 압박을 감소시킴. -
복사 루프 감지: 프로파일 데이타를 사용하여 자주 실행되는 복사 루프를 선택.
-
분기 예측: 프로파일 데이타를 사용하여 컴파일러가 조건 분기 명령어들의 opcode 에 분기 예측 비트들을 설정함으로써 분기 예측을 지원하는 프로세서들의 파이프라인 스톨을 최소화 해주는 방법.
프로파일 피드백 메카니즘을 이용하는 단계는 다음과 같습니다:
-
프로파일 데이타 수집을 위한 컴파일
-
시범 적인 실행/프로파일 피드백 데이타를 수집하기 위한 실행
-
프로파일 피드백을 이용하여 애플리케이션을 재구축
이러한 단계들의 소개는 profile feedback을 사용하여 애플리케이션 성능 향상 시키기 에서 보실 수 있습니다. 이 글은 이러한 단계들을 예제와 함께 자세히 소개하고 있습니다.
1.1. 프로파일 데이타 수집을 위한 컴파일
-xprofile=collect 컴파일러 옵션을 이용하여 애플리케이션을 구축합니다. 이 단계에서 오브젝트 코드는 프로파일 데이타를 위해 설치 됩니다. 예를 들어 오브젝트 코드에 카운터가 삽입되어서 코드가 몇번이나 실행되었는지 알 수 있도록 합니다. 설치된 오브젝트들은 프로파일된(profiled) 오브젝트라고도 불립니다. 이 코드들은 보통 설치되지 않은 코드에 비해 속도가 느립니다. 이 방법은 오직 프로파일 데이타를 수집할때만 사용하시기 바랍니다.
설치된 바이너리를 실행하면 엔드유저에게는 애플리케이션에 아무런 변화가 없어 보이지만, 프로파일 데이타가 실행의 결과로 수집됩니다. 이러한 데이타는 컴파일러의 FBO 사용 단계에서 최적화된 바이너리를 생성하는데 사용 됩니다.
FBO 는 코드가 최적화 레벨 2, 혹은 그 이상에서 컴파일 되기를 요구합니다. 만약 아무런 최적화 레벨이 컴파일시에 지정되지 않는다면 컴파일러는 레벨 2 최적화 (예를 들어 -O2) 를 기본값으로 사용합니다. 컴파일러는 이러한 -xprofile=collect 옵션을 사용할때 이러한 최적화를 런타임시 코드의 정확한 정보를 기록하기 위해 무시할 수도 있습니다. 하지만 피드백 기반의 최적화의 두 가지 단계 모두에서 -xprofile를 제외한 나머지의 경우, 정확한 컴파일러 플래그를 명시해 주는 것을 권장합니다.
다음의 예제는 썬 스튜디오 C 컴파일러를 사용하여 설치 바이너리를 생성하는 단계들에 대해 설명합니다.
% cat bubblesort.c
#include <stdio.h>
#include <stdlib.h>
#define COUNT 50
void swap (int *Array, int i, int j)
{
int temp;
temp = Array[i];
Array[i] = Array[j];
Array[j] = temp;
}
void bubblesort(int *Array, int length)
{
int i, j;
for (i = 0; i < length; ++i)
{
for (j = (i + 1); j < length; ++j)
{
if (Array[i] > Array[j])
{
swap (Array, i, j);
}
}
}
}
int main()
{
int i, *Array;
Array = (int *) malloc (sizeof (int) * COUNT);
for (i = COUNT; i > 0; --i)
Array[COUNT - i] = i;
bubblesort(Array, COUNT);
for (i = 0; i < COUNT; ++i)
printf("\nArray[%d] = %d", i, Array[i]);
return (0);
} |
프로파일 데이타 수집을 활성화 하기 위해 이 코드를 -xprofile=collect 와 -xO2 옵션을 이용해 컴파일 하시기 바랍니다.
% cc -o bubblesort -xO2 -xprofile=collect bubblesort.c
-xprofile_ircache[=path] 를 -xprofile=collect|use 과 함께 사용함으로써 수집 단계에서 저장된 컴파일 데이타(IR) 을 재사용하여 사용 단계 동안에 컴파일 시간을 향상시킬 수 있습니다. 저장된 데이타는 디스크 용량을 크게 소비할 수도 있음을 알아두시기 바랍니다.
좀 더 자세한 정보는 C compiler options reference in the C User's Guide 에 있는 -xprofile=p 과 -xprofile_ircache 옵션 페이지를 참고 바랍니다.
1.2. 시범 적인 실행/프로파일 피드백 데이타를 수집하기 위한 실행
설치된 바이너리 (-xprofile=collect 옵션으로 컴파일된 바이너리)를 하나 혹은 그 이상의 대표저인 워크로드와 함께 실행시킵니다. 만약 워크로드가 대표적이라면, 시범 실행시에 발생한 일반적인 분기들은 모두 실제 워크로드에서도 정상적으로 일어날 것입니다.
일반적으로, 프로그램을 오직 하나의 입력 파일과 함께 실행시키면 단순히 그 입력 파일 하나만 실행시켜서 괜찮은 프로파일 데이타를 얻을 수 있습니다. 그러나 만약 프로그램의 서로 다른 부분을 실행 시킬 수 있는 다양한 입력을 가질 수 있는 범용의 애플리케이션을 작성한다면, 반드시 프로그램이 받을 수 있는 서로 다른 종류의 대표 샘플 입력을 선택해야 합니다. 오직 몇몇 종류의 입력만을 사용 하는 것은 컴파일러를 편향되게 함으로써 원하는 결과를 얻지 못할 수 있습니다. 그러므로 시범적으로 실행할 하나 혹은 그 이상의 워크로드 조합을 찾는 것이 좋은 결과를 얻을 수 있습니다.
이 단계에서, 컴파일러에 의해 설치된 코드는 모든 분기들에게서 분기들의 빈도 수와 모든 기본 블럭들의 카운트를 수집합니다. 실행의 결과로 프로그램명에 .profile 확장자가 붙은 디렉토리가 생성될 것입니다. <program>.profile 디렉토리에 있는 feedbin 파일은 후에 -xprofile=use 옵션을 사용하여 소스코드가 다시 컴파일될때의 최적화를 위해 각 블럭들의 실행 빈도를 저장합니다. feedbin 파일은 프로파일 피드백 파일로 참조될 수 있습니다.
프로파일 데이타 수집은 부가적입니다. 즉 만약 프로파일된 실행파일을 비슷하거나 혹은 다른 입력과 함께 한번 이상 실행한다면 수행 결과는 이전의 수행결과에 계속해서 더해지게 됩니다. 그러므로 프로파일 데이타는 모든 profiled 실행 파일의 결과가 합쳐진 것입니다.
그러나 한 가지 주의할 점이 있습니다. 만약 이전의 시범 실행시에 생성된 프로파일 데이타를 가지고 있고 이 프로그램을 -xprofile=collect 을 이용해 재 컴파일 후 재실행 한다면, 컴파일러가 설치한 코드는 이것을 다른 프로그램이라고 감지하고 기존 데이타를 덮어 쓰게 됩니다.
기본적으로 <program>.profile 디렉토리는 실행파일이 실행된 동일한 디렉토리에 생성될 것입니다. 만약 디렉토리를 바꾸고 싶다면 SUN_PROFDATA_DIR 환경 변수를 다음과 같이 이용할 수 있습니다.
계속해서 버블 소트 예제를 살펴 봅시다:
% ./bubblesort Array[0] = 1 Array[1] = 2 .. .. Array[48] = 49 Array[49] = 50 % ls -dF *.profile bubblesort.profile/ % ls -dF /tmp/*.profile No match % setenv SUN_PROFDATA_DIR /tmp % ./bubblesort Array[0] = 1 Array[1] = 2 .. .. % ls -dF /tmp/*.profile /tmp/bubblesort.profile/ |
1.2.1 모든 프로파일된 프로세스들을 위한 하나의 feedbin
기본적으로 프로파일러 쓰레드는 하나의 프로파일 피드백 파일(feedbin) 을 각 프로파일된 실행파일들을 위해 생성합니다. 기본 설정은 매우 적은 실행파일들을 가지는 작은 프로그램 혹은 애플리케이션에 적당합니다. 그러나 수백가지의 실행파일들을 가지는 대용량의 애플리케이션들은 너무 많은 수의 프로파일 피드백 파일을 가지게 됨으로써 -xprofile=use:<path_to_profdir> 옵션을 사용하여 최적화된 바이너리를 생성할 때 불편함을 줄 수 있습니다.
예를 들어, 만약 애플리케이션의 20개의 실행파일로 이루어져 있다면 컴파일 라인에 20개의 -xprofile=use 플래그를 아래와 같이 적어줘야 합니다:
% cc -xO2 -xprofile=use:feedback1 -xprofile=use:feedback2 [...] \
-xprofile=use:feedback19 -xprofile=use:feedback20 -o optimalbin <sourcefile>.c
이 과정에는 다음과 같은 주요한 두 가지 불편함이 따르게 됩니다:
-
만약 make 파일이 모든 컴파일러 옵션을
CFLAGS같은 환경 변수에서 가져온다면, 쉘의 환경변수당 문자수 제한에 의해-xprofile=use의 모든 인스턴스가 지정이 되지 못할 수도 있음. -
지나치게 많은
-xprofile=use의 예시로 인하여, 컴파일 라인이 너무 길어져서 보기 안좋을 수 있음.
이러한 불편함을 해결하기 위하여, 프로파일의 수집 단계에서 컴파일러가 지원하는 환경변수 SUN_PROFDATA_DIR 와 SUN_PROFDATA 를 사용할 것을 권장합니다. 이것은 프로파일러가 모든 프로파일된 프로세스 들에서 얻어지는 데이타를 하나의 feedbin 파일에 저장하도록 합니다. 만약 이러한 환경변수들이 지정되어 있다면 프로파일러는 데이타를 SUN_PROFDATA_DIR 디렉토리 밑에 SUN_PROFDATA 에 의해 지정된 파일에 저장합니다. 즉 모든 프로세스들의 프로파일 데이타가 $SUN_PROFDATA_DIR/$SUN_PROFDATA 에 쓰여지게 됨을 의미합니다.
다음의 간단한 예제는 기본 동작에 대해 설명하고 SUN_PROFDATA 과 SUN_PROFDATA_DIR 환경 변수를 이용한 동작에 대해 보여주고 있습니다.
예제:
|
1.2.1.1 기본 동작
다음의 예제에서 각 실행파일당 한개씩 두개의 프로파일이 있음을 확인하시기 바랍니다. a.profile 은 실행 파일 "a" 의 프로파일 정보를 b.profile 은 실행파일 "b" 의 프로파일 데이타를 가지고 있습니다.
|
피드백 데이타를 사용하기 위해 프로그램은 다음과 같이 컴파일 되어야 합니다:
% cc -xO2 -xprofile=use:/tmp/default/a -o a a.c% cc -xO2 -xprofile=use:/tmp/default/b -o b b.c
1.2.1.2 싱글 피드백 파일 요청하기
싱글 피드백 파일은 다음과 같이 요청 됩니다:
% setenv SUN_PROFDATA_DIR /tmp/consolidate% setenv SUN_PROFDATA singlefeedbin.profile
런타임동안 프로파일러 쓰레드는 SUN_PROFDATA 와 SUN_PROFDATA_DIR 값을 읽고 모든 프로파일 데이타를 /tmp/consolidate/singlefeedbin.profile 디렉토리 밑에 하나의 feedbin 파일에 저장합니다.
예제:
|
singlefeedbin.profile 이 "a" and "b" 모두의 프로파일 데이타를 가지고 있음을 알 수 있습니다. 만약 좀 더 많은 수의 프로파일된 프로세스가 있다면 그들의 데이타는 모두 feedbin 파일에 추가되어질 것입니다.
프로파일을 사용하기 위해 단순히 다음을 실행합니다:
% cc -xO2 -xprofile=use:/tmp/consolidate/singlefeedbin -o a a.c% cc -xO2 -xprofile=use:/tmp/consolidate/singlefeedbin -o b b.c
그러나 이렇게 하나의 프로파일 피드백 파일에 쓰는 것은 오직 삽입된 오브젝트들이 각각의 프로파일된 프로세스들에 서로 의존적으로 다루어질 때에만 도움이 된다는 것에 주의할 필요가 있습니다. 위의 예제의 목적은 오직 프로파일러가 단 하나의 피드백 파일에 프로파일 데이타를 저장하도록 요청하는 것을 보여주기 위한 것입니다.
1.2.2 비동기 프로파일 데이타 수집
기본적으로 프로파일 데이타 수집은 동기적입니다. 프로파일러 쓰레드는 공유 라이브러리의 종료를 기다리고 또한 프로파일 데이타을 피드백 파일에 쓰기 전에 프로세스가 exit() 을 호출 하는 것을 기다립니다. 이 방법에서, 프로파일 데이타를 얻기 위해서는 프로세스가 종료되어야 합니다. 결과적으로 멀티 쓰레드 애플리케이션은 종종 쓰레드간에 발생하는 레이스 컨디션등에 의해 데이타 손실이 발생할 수 있습니다. 또한 모든 애플리케이션 특히 멀티 쓰레드 애플리케이션은 보통 깨끗하게 종료되도록 디자인 되지 않았습니다. 만약 몇몇 프로파일된 프로세스가 exit() 을 호출 하는 대신 다른 방법을 사용해서 종료가 된다면 (예를 들어 SIGKILL 을 시그널링 하는 것 같은) 이러한 프로세스에서는 사용할만한 프로파일이 얻기 어렵습니다. 만약 프로파일된 프로세스가 동적으로 로드 되고 다른 라이브러리를 dlopen(), dlclose() 같은 시스템 콜을 이용해서 언로드 한다면 이것은 간접 호출 프로파일링으로 유도하게 되고 프로파일 데이타를 수집하는데 문제가 생기게 됩니다.
위에서 말한 문제를 완화시키기 위해 우리는 프로세스가 깨끗하게 종료 되지 않을때를 대비해 실행중인 프로세스의 프로파일 데이타를 얻을 수 있는 메카니즘이 필요 합니다. 비동기 프로파일 데이타 수집 기능은 썬 스튜디오 11 컴파일러 버전에서 추가되었습니다. 그 다음에 썬 스튜디오 9, 10으로 백포트 되었습니다. 패치 115983-06 (혹은 이후) 을 스튜디오 9에 적용하고 117832-06 (혹은 이후) 를 스튜디오 10에 적용하면 이러한 기능들을 사용할 수 있습니다. 결과적으로 싱글/멀티 쓰레드 애플리케이션에서 좀더 좋은 프로파일 데이타를 프로세스의 종료 상태에 상관없이 얻을 수 있습니다.
1.2.2.1 비동기 프로파일 데이타 수집 활성화
비동기 프로파일 수집은 기본적으로 활성화되지 않습니다. 활성화 하기 위해서는 SUN_PROFDATA_ASYNC_INTERVAL 환경 변수를 애플리케이션을 실행하기 전에 지정해야 합니다. 만약 SUN_PROFDATA_ASYNC_INTERVAL 이 애플리케이션의 시작시에 양의 정수 n 으로 설정되었다면 프로파일러 쓰레드는 프로파일 데이타를 매 n 초 마다 주기적으로 수집하고 해당 feedbin 파일을 업데이트 합니다. n 은 주기적인 프로파일 스냅샷의 시간 간격으로 초 단위 입니다.
스냅샷 데이타가 수집되면 프로파일러는 다음과 같은 이름을 가진 디렉토리의 프로파일 데이타에 기록합니다: <procname>.<hostname>.<pid>[.profile]
여기서:
<procname> 는 프로파일 된 프로세스의 이름
<hostname> 는 프로파일 된 프로세스를 실행하고 있는 머신의 호스트 이름
<pid> 는 프로파일된 프로세스의 프로세스 id
.profile 은 SUN_PROFDATA 환경변수에 <dir_name> 이 지정되지 않았을 경우 프로파일 디렉토리의 이름에 무조건 추가 됩니다.
알아둘 점은 프로파일러 쓰레드는 프로파일 스냅샷을 오직 프로파일러 쓰레드가 초기화된 프로세스 내에서만 수집합니다. fork 된 프로세스는 프로파일러 쓰레드를 상속받지 못합니다.
수집된 프로파일 데이타는 프로파일 피드백의 사용 단계 에서 컴파일러 옵션을 지정함으로써 사용이 가능합니다: -xprofile=use:<procname>.<hostname>.<pid>. 프로파일 디렉토리는 -xprofile=use 옵션에서 지정하기 전에 사용자 마음대로 변경할 수 있습니다.
1.2.2.2 프로세스당 여러개의 프로파일 스냅샷 생성
비동기 프로파일 수집은 또한 프로세스당 하나 이상의 프로파일 데이타 수집이 가능하도록 해 줍니다. 만약 환경 변수 SUN_PROFDATA_ASYNC_SEQUENCE 가 지정되었고 양의 정수 값, num_snapshots ≥ 1, 로 지정 되었다면 프로파일러는 다음과 같은 이름 형식의 프로파일 스냅샷 들을 생성합니다: <procname>.<hostname>.<pid>.<n>[.profile]
여기서:
<n> 은 양의 정수 범위를 가짐 [1..스냅샷의_숫자].
순차적인 프로파일 스냅샷들이 <procname>.<hostname>.<pid>[.profile] 디렉토리의 파일에 프로세스의 실행 동안 계속적으로 추가 됩니다.
SUN_PROFDATA_ASYNC_SEQUENCE 를 지정하여 시간 순서 대로 프로파일 스냅샷을 생성하는 경우 -xprofile=use 를 이용 컴파일 하여 좋은 성능을 얻기 위해 주어진 애플리케이션의 프로파일 데이타를 얼마나 수집해야 하는지 결정하기 위해 사용 됩니다.
예제:
프로그램 mtserver 가 -xprofile=collect 를 이용해 컴파일 되었다고 가정합니다. 비동기 프로파일 데이타 수집은 다음과 같이 이루어 집니다:
|
이 예제는 프로세스 8529 가 실행될때 까지 매 30초마다 프로파일 데이타의 스냅샷을 수집합니다. 처음 3개의 스냅샷은 각각 고유의 디렉토리에 저장될 것입니다: /tmp/profile/mtserver.v890appserv.8529.1.profile,
/tmp/profile/mtserver.v890appserv.8529.2.profile 그리고/tmp/profile/mtserver.v890appserv.8529.3.profile. 이후의 스냅샷들은 피드백 디렉토리에서 업데이트 될 것입니다:
/tmp/profile/mtserver.v890appserv.8529.profile.
프로파일 데이타 수집시에 주의 메세지를 보기 원한다면 환경변수 SUN_PROFDATA_VERBOSE 를 지정하기 바랍니다. 멀티 쓰레드 프로그램에서 -xprofile=collect 을 이용해서 프로그램이 컴파일 됐을때 쓰레드 카운트가 증가 함을 관찰하시기 바랍니다. 사용자가 생성하지 않은 추가적인 쓰레드는 프로파일러 쓰레드로써 쓰레드들을 컨트롤 하기 위해 쓰레드에 추가적인 코드들을 더한 것입니다.
1.3. 프로파일 피드백을 이용해 애플리케이션을 재구축
프로파일된 프로세스로 부터 프로파일 데이타를 얻었으면 이제 이 것을 다음의 플래그를 이용해서 컴파일러에 전달합니다: -xprofile=use:<path_to_profdir>. 컴파일러는 이 데이타를 이용해 애플리케이션의 코드를 좀 더 향상된 최적화를 이루기 위해 사용합니다. 프로파일 데이타 디렉토리를 전달하는 것을 잊지 마시기 바랍니다 -- 만약 -xprofile=use 만을 사용했다면 컴파일러는 프로파일 디렉토리가 어떤 것인지 알수 없습니다;그러므로 a.out.profile 을 기본적으로 찾게 됩니다. 프로파일 데이타 디렉토리 이름을 지정할때 .profile 을 붙일 필요는 없습니다. 버블 소트 예제에서 컴파일 라인에 -xprofile=use:bubblesort.profile 혹은 -xprofile=use:bubblesort 을 붙이는 것은 둘다 괜찮습니다.
-xprofile=collect 에서 -xprofile=use 로 바뀌는 -xprofile 을 제외하고 소스 파일들과 다른 컴파일러 옵션은 반드시 프로파일된 오브젝트를 컴파일했을때 사용했던 것과 정확히 똑같아야 합니다.
만약 -xprofile=collect 과 -xprofile=use 가 컴파일 라인에 같이 명시되어 있다면 제일 오른쪽의 -xprofile 옵션이 적용 됩니다.
만약 이전에 -xprofile=collect 옵션을 이용해서 컴파일 했던 오브젝트 파일의 디렉토리와 다른 디렉토리에서 -xprofile=use 를 이용해 오브젝트를 컴파일 한다면 반드시 컴파일 라인에 -xprofile_pathmap=<collect_prefix>:<use_prefix> 옵션을 추가 시켜서 컴파일러가 프로파일 데이타를 찾을 수 있도록 해야 합니다. collect-prefix 는 오브젝트 파일이 -xprofile=collect 를 이용해서 컴파일 됐을때의 디렉토리 경로입니다;그리고 use-prefix 는 오브젝트 파일이 -xprofile=use 를 이용해서 컴파일 됐을때의 디렉토리 경로입니다. C 컴파일러 옵션 참고자료를 통해 좀 더 자세한 정보와 -xprofile_pathmap 컴파일러 옵션에 대해 알아보시기 바랍니다.
버블 소트 예제를 계속 해서 설명합니다:
|
중요한 정보:
프로파일 피드백을 사용했을때의 애플리케이션의 퍼포먼스를 측정하고 이것을 구축 환경에 투입시키기 전에 사용하기전 퍼포먼스와 비교해보시기 바랍니다. 왜냐하면 애플리케이션 코드를 두번 컴파일 하기 때문에, 다른 디버깅 이나 튜닝이 완료된 다음에만 주로 사용되어지며, 제품에 탑재되거나 고객들에게 배포되기 전 마지막 단계이기 때문입니다.
1.3.1 여러개의 프로파일과 함께 컴파일
썬 스튜디오 컴파일러는 컴파일 라인에 -xprofile=use:<path_to_profdir> 옵션을 통해 다중의 프로파일들을 받을 수 있습니다. -xprofile=use:<path_to_profdir1>:<path_to_profdir2>..<path_to_profdirn> 은 컴파일 에러가 발생합니다.
예를 들어:
% cc -xO2 -xprofile=use:/tmp/prof1.profile -xprofile=use:/tmp/prof2.profile
컴파일러가 컴파일 라인에서 복수의 프로파일을 받게 되면 모든 프로파일 데이타는 코드 변환이 수행되기 전에 프로파일 피드백 데이타에 기반하여 하나로 합쳐지게 됩니다.
1.3.2 실행 카운트 추출
만약 프로파일 피드백 데이타에 기반한 코드 변환에 대해 궁금하시다면, 다음의 코드 생성 (cg) 옵션을 사용해서 어셈블리 목록의 블럭 실행 횟수를 확인해보시기 바랍니다.
C (cc):path_to_profdir
-xprofile=use:<> -Wc,-assembly,-Qcg-V
C++ (CC) and Fortran (f95):path_to_profdir
-xprofile=use:<> -Qoption cg -assembly,-Qcg-V
-assembly 옵션은 오브젝트 파일과 동일한 basename 과 dirname 을 가진 .s 파일을 생성할것입니다. (예를 들어 bubblesort.o 는 같은 디렉토리에 bubblesort.s 를 가질 것임). -Qcg-V 옵션은 생성된 .s 파일의 어셈블러 코드에 좀 더 많은 정보를 추가시켜 줄 것입니다. 만약 -xprofile=use 옵션이 지정된다면 이 정보는 <path_to_profdir> 의 실행 카운드 정보도 포함하게 됩니다.
예제:
|
1.3.2.1 대안
코드 커버리지 분석 툴인 tcov 는 블럭들과 명령어들의 실행 빈도를 찾는데 사용할 수 있습니다. 만약 소스 코드가 -g 혹은-g0 디버그 옵션과 함께 컴파일 된다면 썬 스튜디오 er_src 유틸리티를 통해 코드 변환에 대해 컴파일러가 삽입한 정보들을 읽어낼 수 있습니다.
썬 스튜디오의 퍼포먼스 분석 문서를 통해 좀 더 자세한 정보를 얻으시기 바랍니다.
2. 엔터프라이즈 애플리케이션의 패치 작성
-xprofile=collect 를 이용하여 애플리케이션을 재구축 하는 과정을 피할 수 있는 방법을 설명합니다.만약 애플리케이션이 매우 크고 오직 몇몇 오브젝트들만 수정 되었다면 패치 형식으로 배포될 재구축 된 오브젝트들만을 프로파일 합니다. 그러나 의미 있는 프로파일 데이타를 수집하기 위해서는 재구축된 실행파일이나 공유 라이브러리를 포함한 모든 오브젝트 파일의 -xprofile=collect 버전이 있어야 합니다. 예를 들어 만약 실행파일 mtserver 가 a.o 와 b.o 를 링크하여 구축되었다면 이러한 모든 오브젝트들을 -xprofile=collect 를 이용해서 컴파일 하고 재링크해서 새로운 mtserver 를 구축해야 합니다. 그러면: (i) 기존의 바이너리를 새롭게 구축된 바이너리로 모두 교체하고; (ii) 다시 새로운 시범 실행을 한 후에 전체 구축에 대한 프로파일 데이타를 수집하고; (iii) 마지막으로 모든 바이너리를 포함한 오브젝트 파일을 -xprofile=use 로 재컴파일 한 다음 고객에게 배포되는 실제 바이너리를 재링크하여 패치로 제공합니다.
예제:
공유 라이브러리 libABC.so 가 프로파일 피드백에 의해 구축 되었고 A.o, B.o 그리고 C.o 오브젝트를 링크함으로써 만들어 졌다고 가정해 봅시다. 만약 오브젝트 A & B 가 후에 수정/개선 되었고 libABC.so 를 프로파일 피드백을 이용해서 다음과 같이 재구축 했습니다:
-
오브젝트 A 와 B 를
-xprofile=collect를 이용해 컴파일함. -
A.o(새로운 오브젝트),B.o(새로운 오브젝트) 그리고C.o(기존의 오브젝트) 를 링크하여libABC.so를 구축. 링크 라인에 반드시-xprofile=collect컴파일러 플래그를 지정해줌. -
이전의 수집 구축에 있었던
libABC.so을 새롭게 구축한 것으로 교체함. 여기서 이전 버전의 애플리케이션에서 쓰였던 프로파일 데이타가 아직까지 존재 한다고 가정. -
시범 실행을 통해 전체 애플리케이션의 프로파일 데이타를 수집 하고 특히 이전의 시범 실행때 사용했었던 워크로드 사용을 권장 함.
-
A 와 B 오브젝트를
-xprofile=use컴파일러 태그를 이용해 다시 컴파일 하고 단계 #4 에서 새로 수집한 데이타를 이용함. -
A.o(새로운 오브젝트),B.o(새로운 오브젝트) 그리고C.o(기존의 오브젝트) 오브젝트를 이용하여libABC.so를 구축함. 링크 라인에-xprofile=use컴파일러를 지정해 줌. -
libABC.so를 패치의 형태로 고객들에게 배포함.
패치로 배포될 모든 바이너리들은 위의 단계를 계속해서 반복합니다. 만약 여러개의 바이너리가 재구축되어야 하더라도 단계 4번은 오직 한번만 진행될 것입니다. 만약 여러개의 바이너리들이 소스코드의 변경으로 인해 재구축 되어야 한다면 패치로 만들 바이너리들만 구축 하는 것 대신 전체 애플리케이션을 -xprofile=collect 을 사용해서 재구축 할것을 고려하시기 바랍니다.
일반적으로 소스코드 베이스에 변경이 생겼을때 마다 프로파일 데이타를 모을 것을 권장합니다. 그러나 이것은 거대한 용량의 애플리케이션을 사용할때에는 매우 힘든 일입니다. 그러므로 소스 코드 몇줄정도의 변화 등, 어느 정도의 확장단계 까지는 프로파일 데이타의 수집을 건너 뛸것을 권장합니다. 하지만 프로파일 피드백으로 인한 이득은 소스 코드 베이스가 수정되었더라도, 이전의 수집된 프로파일 데이타를 계속 사용하게 되면 시간이 지날 수록 줄어든다는 점을 기억하시기 바랍니다. 그러므로 최적화된 성능을 얻기 위해서는 소스 코드가 패치를 배포할 정도로 크게 변화가 되었을때 다시 프로파일 데이타를 수집하는 것이 좋습니다. 즉 많은 수의 수정된 바이너리 패치를 배포할때를 말하는 것입니다.
3. 기존의 프로파일 데이타를 사용해서 수정된 소스 컴파일하기
기존의 프로파일 데이타가 있는 상황에서 소스 코드의 간단한 변화가 피드백 기반 최적화에 어떠한 방식으로 영향을 주는지 아는 것은 중요 합니다. 프로그램이 스트링 비교 함수 __strcmp, 스트링 길이 계산 __strlen 함수를 구현한 라이브러리 libstrimpl.so 와 같이 링크 됐다고 가정해 봅시다.
예제:
Assume that the shared library,
|
라이브러리의 다음 배포를 위하여 새로운 버전에서 스트링을 앞뒤로 바꿔주는 __strreverse 라이브러리가 추가 되었습니다. 이제 만약 __strreverse 루틴을 라이브러리에 통합시킨 다음에 프로파일 데이타 수집을 하지 않음으로써 어떠한 일이 발생하는지 보겠습니다. 프로그래머는 소스 코드안에 독립적인 루틴들의조직을 별로 신경 쓰지 않을 것이기 ?문에 새로운 루틴은 소스 파일의 아무 곳에나 위치할 수 있습니다.
사례 1: 루틴이 현존하고 있는 루틴들의 제일 밑에 추가되었을 때
|
만약 새로 추가된 코드를 위한 프로파일 데이타를 수집하지 않으려 한다면 새로운 코드를 소스 파일의 제일 마지막에 집어 넣는 것을 권장합니다. 이렇게 함으로써 현재 존재하는 프로파일 데이타는 일관적이게 되고 컴파일러에 의해 수정되지 않은 코드들을 최적화 하는데 사용 될 수 있습니다. 새로운 루틴에 프로파일 데이타가 사용가능하지 않기 때문에 컴파일러는 오직 -xprofile 옵션이 없을때 수행하는 단순 최적화 작업을 수행합니다.
사례 2: 루틴의 소스 코드의 중간에 추가되었을 때
|
컴파일러는 블럭의 라인 번호와 그 라인번호의 실행 카운트를 피드백 (feedbin) 파일에서 읽어 옵니다. 결과적으로 루틴에 추가된 새로운 코드는 프로파일 데이타를 일관성이 없도록 만듭니다. 또한 다른 루틴들의 위치가 새로 추가된 코드 및에 존재하게 됨으로써 그들의 프로파일 데이타 또한 일관성이 없어 집니다. 그러므로 이로 인해 발생하는 에러들을 회피하기 위해 컴파일러는 각 루틴의 프로파일 데이타를 무시합니다.
새로운 코드가 소스 파일의 제일 상위에 추가 되더라도 위와 같은 문제가 발생할 것임은 매우 명백합니다. 이러한 작업은 이 오브젝트를 위한 프로파일 데이타를 사용하지 못하도록 만듭니다. 다음의 예제의 오류 메세지를 보면 좀 더 명확하게 이해할 수 있을 것입니다.
사례 3: 루틴의 소스 파일의 제일 상위에 추가되었을 때
|
결론: 만약 새롭게 프로파일 데이타를 수집하는 대신 기존에 존재하는 프로파일 데이타를 사용하려면 언제나 새로운 코드를 소스 코드의 제일 아래에 위치 시켜서 현재 존재하는 루틴들을 위한 데이타 일관성을 유지할 수 있도록 해 줍니다.
4. 프로파일 데이타를 사용할 수 있는 다른 컴파일러 옵션
-xipo 는 크로스파일 최적화를 수행합니다 -- 최적화가 여러 소스 파일에 걸쳐서 확장됩니다. 이러한 종류의 최적화의 한 예로 하나의 소스 파일에 있는 루틴을 다른 파일에 인라인시키는 방법이 있습니다. 프로파일 데이타의 존재로 인해 어떠한 루틴들이 인라인할 가치가 있는 지 알아 내는 것은 훨씬 쉬워 졌습니다.옵션 -xlinkopt 는 컴파일러가 링크 타임 최적화를 수행하도록 합니다. 이러한 컴파일의 마지막 단계에서는, 생성된 코드들의 모든 정보를 이용하여 코드 레이아웃을 최적화 합니다. 이것은 특히 거대한 규모의 코드에서 자주 실행되는 코드를 한군데 묶는 코드 재배치 작업을 통해서 성능 향상을 얻을 수 있도록 도와 줍니다.
기술문서 코드 레이아웃을 변경하여 애플리케이션의 퍼포먼스를 향상 시키는 방법 을 통해 좀 더 자세한 정보를 알아보시기 바랍니다.
5. 이종 플랫폼들을 위한 프로파일 데이타 포팅 가능성
6. 피드백기반 최적화의 대안
binopt 를 썬 스튜디오 11 컴파일러 스윗에 처음 소개했습니다. 만약 피드백 기반 최적화를 사용하는 것이 적당하지 않을 경우에 혹은 비대표적인 워크로드의 사용으로 인해 효과적이지 않을 경우엔 binopt 를 대안으로 사용해서 애플리케이션의 성능을 향상시킬 수 있습니다.Sun Studio Binary Code Optimizer 기술 문서를 참고 해서 binopt 의 사용에 대해 알아보시기 바랍니다.
7. 요약
-
컴파일러의 버전에 의존함으로 다음의 최신 패치들이 설치 되어 있는지 확인함:
Studio 9은 115983, 스튜디오 10은 117832; 그리고 스튜디오 11은 120760 . -
애플리케이션을 다음의 컴파일러 플래그를 이용해서 구축:
-xprofile=collect,-xO2혹은 더 높게,-xipo,-xlinkopt, 그리고 다른 최적화 플래그들.예를 들어:
% cc -xO2 -xprofile=collect -xipo -xlinkopt -o application application.c
-
애플리케이션을 하나 혹은 그 이상의 대표 워크로드와 함께 실행시켜서 프로파일 피드백 데이타를 비동기적으로 수집.
% mkdir /tmp/myapp % setenv SUN_PROFDATA_ASYNC_INTERVAL 30 % setenv SUN_PROFDATA_DIR /tmp/myapp % ./application args
-
애플리케이션을
-xprofile=use을 이용하여 재구축 하고 최적화 레벨을-xO2혹은 그이상,-xipo,-xlinkopt와 다른 최적화 플래그들을 같이 사용.% cc -xO2 -xprofile=use:/tmp/myapp.profile
-xipo -xlinkopt -o application application.c
참고자료와 추가자료
-
Use Profile Feedback To Improve Performance, by Darryl Gove and Chris Aoki
-
Improving Code Layout Can Improve Application Performance, by Darryl Gove
-
The Sun Studio Binary Code Optimizer, by Sheldon Lobo
감사의 글
저자에 관하여
"개발자코너" 카테고리의 다른 글
- C++ ABI의 안정성: 프로그래밍 언어의 진화 (댓글 1개 / 트랙백 0개) 2006/02/23
- DTrace를 사용하여 유저가 조정하는 애플리케이션 크래쉬 데이타 정보 모으기 (댓글 1개 / 트랙백 0개) 2006/08/23
- 솔라리스 상에서 "자바 GNOME" 바인딩을 이용해서 개발하기 (댓글 3개 / 트랙백 0개) 2007/04/20
- libumem 라이브러리를 이용하여 어플리케이션 내의 메모리 관리 버그를 잡아 내는 방법 (댓글 1개 / 트랙백 1개) 2006/03/23
- 솔라리스 디바이스 드라이버 작성을 원하십니까? (댓글 0개 / 트랙백 0개) 2008/09/18
- dbx가 프로세스나 코어 파일을 읽지 못하는 이유 (댓글 1개 / 트랙백 0개) 2006/01/23
- dbx를 통한 Java 어플리케이션 디버깅: Java 코드를 위한 업계 최고 수준의 디버깅 (댓글 1개 / 트랙백 0개) 2005/11/23
- OpenMP 소개: 포터블한, 공유 메모리 멀티프로세서 환경에서의 병렬 프로그래밍 API (댓글 10개 / 트랙백 1개) 2007/07/16
- SPOT을 이용한 손쉬운 어플리케이션 성능 분석 (댓글 1개 / 트랙백 0개) 2006/06/23
- Dmake 를 사용하여 어플리케이션 빌드의 속도를 향상시키기 (댓글 0개 / 트랙백 0개) 2008/10/15
댓글을 달아 주세요
짬짬이 좋은 공부 하고 갑니다. 좋은 자료 많이 많이 올려주세요.
2007/09/12 22:03알아가는 재미가 좋읍니다^^
2007/09/18 14:40좋은 정보 감사해요~
2007/09/19 04:01