You are here

LabVIEW 성능 및 메모리 관리 1

이 어플리케이션 노트에서는 VI의 실행 시간에 대한 데이터를 보여주고 단일 스레드, 멀티스레드 및 멀티프로세서 어플리케이션을 모니터링하는 기능인 성능 프로파일러에 대해 설명합니다. 여기서는 또한 런타임 속도와 메모리 사용에 영향을 미치는 원소들에 대해서도 설명합니다.

 

목차:

  • VI 성능 프로파일링
  • VI 실행 속도

 

VI 성능 프로파일링
VI 성능 프로파일러는 어플리케이션이 어디에 시간을 할애하는지 그리고 어떻게 메모리를 사용하는지 결정하는 강력한 도구입니다. Profile 창에는 시스템에 있는 각 VI의 시간과 메모리 사용을 보여주는 대화식 테이블 형식의 디스플레이가 있습니다. 테이블의 각 행에는 특정 VI에 대한 정보가 들어 있습니다. 각 VI에서 소요된 시간은 여러 범주로 나누어져 요약됩니다. Profile 창에서는 VI를 실행할 때마다 소요되는 최소, 최대 및 평균 시간을 계산합니다. 대화식 테이블 형식의 디스플레이에서는 이 정보의 전체나 일부를 표시하고, 여러 범주별로 정렬하고 특정 VI로부터 호출된 SubVI의 성능 데이터를 살펴볼 수 있습니다.

도구>>프로파일>>성능과 메모리(Tools»Advanced»Profile Vis)를 선택하여 Profile 창에 액세스합니다. 다음 그림은 이미 사용되고 있는 창의 예를 나타냅니다.

 

  

수집 프로세스에서는 VI 실행 시간에 상당한 오버헤드를 부가할 수 있으므로 메모리 사용 정보의 수집은 선택적입니다. Profile Memory Usage 체크박스를 적절히 표시하여 프로파일러를 시작하기 전에 이 데이터를 수집할 지 여부를 선택해야 합니다. 일단 프로파일링 세션이 진행 중이면 이 체크박스를 변경할 수 없습니다.

Profile 창에는 다음과 같은 버튼이 있습니다.
Start ‐ 성능 데이터 수집을 활성화합니다. 부분적 실행이 아닌 전체 VI 실행을 측정하려면 어플리케이션이 실행되고 있지 않을 때 프로파일링 세션을 시작하는 것이 가장 좋습니다.  
Snapshot ‐ 현재 사용 가능한 데이터를 봅니다. 이 버튼은 메모리의 모든 VI에서 데이터를 수집하여 테이블 형식의 디스플레이로 표시합니다.
Save ‐ 현재 표시된 데이터를 디스크에 탭으로 구분된 텍스트 스프레드시트 파일로 저장합니다. 이 데이터는 나중에 스프레드시트 프로그램에서 또는 VI를 통해 볼 수 있습니다.

 

결과 보기

테이블에서 정보의 부분만 표시하도록 선택할 수 있습니다. 어떤 기본 데이터는 항상 보이지만 통계, 상세정보 및 (활성화된 경우) 메모리 사용은 Profile 창에서 해당 체크박스를 선택 또는 선택 해제하여 표시 여부를 선택할 수 있습니다. 성능 정보도 글로벌 VI에 표시됩니다. 그러나 이 정보에는 때때로 아래의 카테고리별 특정 섹션에 설명되어 있듯이 약간 다른 해석이 필요합니다.

테이블 형식 디스플레이에서 SubVI의 이름을 더블 클릭하면 SubVI에 대한 성능 정보를 볼 수 있습니다. 이 때 새로운 로우가 VI의 이름 바로 아래 나타나며 여기에는 VI의 SubVI 각각에 대한 성능 정보가 들어 있습니다. 글로벌 VI의 이름을 더블 클릭하면 프런트패널의 개별 컨트롤에 대한 새로운 로우가 나타납니다. 원하는 컬럼의 헤더를 클릭하면 테이블 형식의 디스플레이로 데이터의 로우를 정렬할 수 있습니다. 현재 정렬 컬럼은 볼드체의 헤더 제목으로 표시됩니다.

VI의 타이밍은 VI가 완료되는 데 걸린 경과 시간과 항상 일치할 필요는 없습니다. 왜냐하면 멀티스레드 실행 시스템에서는 2개 이상의 VI 실행을 인터리빙할 수 있기 때문입니다. 또한 사용자가 대화상자에 응답하는데 소요된 시간이나 블록다이어그램의 Wait 함수에 소요된 시간 또는 마우스 클릭을 확인하는 데 소요된 시간 등 어느 VI에도 기인하지 않는 일정량의 오버헤드도 있습니다.

 

성능 데이터의 처음 3개 컬럼에 항상 표시되는 기본 정보는 다음 항목으로 구성됩니다.

  • VI 시간 – VI의 코드를 실제로 실행하여 데이터를 표시하는 데 소요되는 총 시간 뿐 아니라 사용자가 프런트패널 컨트롤(있는 경우)과 인터페이스하는 데 소요되는 시간. 이 요약 시간은 다음 섹션에서 설명되는 타이밍 상세정보에서 하위 카테고리로 나누어집니다. 글로벌 VI의 경우에 이 시간은 데이터를 모든 컨트롤에서 또는 모든 컨트롤로 복사하는 데 걸리는 총 시간을 말합니다. 각 컨트롤에 대한 타이밍 정보를 보려면 글로벌 VI의 이름을 더블 클릭합니다.
  • SubVI 시간 – VI의 모든 SubVI에서 소요되는 총 시간. 이 시간은 VI의 모든 피호출자뿐 아니라 SubVI의 피호출자 등에 소요되는 VI 시간(위에서 설명)의 합계를 말합니다.
  • 총 시간 – 총 시간을 계산한 VI 시간 및 SubVI 시간의 합계. Runs ‐ VI가 실행을 완료한 횟수. 글로벌 VI의 경우 이 횟수는 컨트롤이 액세스된 총 횟수를 말합니다.  

 

타이밍 정보

Timing Statistics 체크박스가 선택되어 있으면 다음 컬럼이 테이블 형식 디스플레이에 표시됩니다.

  • # Runs ‐ VI가 실행을 완료한 횟수. 글로벌 VI의 경우 이 횟수는 컨트롤이 액세스된 총 횟수를 말합니다. 
  • Average ‐ 각 실행에 VI에 소요된 시간의 평균. VI 시간을 실행 횟수로 나눈 값입니다.
  • Shortest ‐ 실행에서 VI에 소요된 최소 시간
  • Longest ‐ 실행에서 Vi에 소요된 최대 시간

Timing Details 체크박스가 선택되어 있으면 VI에 소요된 시간을 합계한 여러 타이밍 카테고리의 분석도를 볼 수 있습니다. 사용자 인터페이스가 많은 VI의 경우 이 카테고리는 어떤 작업에 가장 시간이 많이 소요되었는지 결정하는 데 도움이 됩니다.

  • Diagram ‐ VI의 블록다이어그램용으로 생성된 코드만 실행하는 데 소요된 시간
  • Display ‐ 블록다이어그램의 새 값으로 VI의 프런트패널 컨트롤을 업데이트하는 데 소요된 시간
  • Draw ‐ VI의 프런트패널을 그리는 데 소요되는 시간. 그리기 시간에는 다음이 포함됩니다.
    창이 열렸을 때 또는 다른 창에 가렸다가 창이 다시 나타났을 때 프런트패널을 그리는 데 걸리는 시간
    겹치거나 투명한 컨트롤을 그리는 데 걸리는 시간. 이들 컨트롤은 블록다이어그램에서 새 데이터를 받으면 화면의 해당 영역을 무효화하여 해당 영역의 모든 정보가 정확한 순서로 다시 그려질 수 있도록 해야 합니다. 다른 컨트롤은 블록다이어그램에서 새 정보를 받으면 프런트패널에 즉시 그릴 수 있습니다. 그리기 타이밍에 나타나는 대부분 정보(다는 아니지만)를 무효화하고 다시 그리는 데는 더 많은 오버헤드가 수반됩니다.
  • Tracking ‐ 사용자가 VI의 프런트패널과 인터페이스하고 있는 동안 마우스를 추적하는 데 소요되는 시간. 이 시간은 그래프를 확대 및 축소하거나 팝업 메뉴에서 항목을 선택하거나 또는 컨트롤을 선택하여 텍스트를 입력하는 것과 같은 일부 작업에 중요할 수 있습니다.
  • Locals ‐ 블록다이어그램의 로컬 변수로 또는 로컬 변수에서 데이터를 복사하는 데 소요되는 시간. 사용자 경험은 특히 크고 복잡한 데이터와 관련되어 있을 때 중요할 수 있다는 것을 보여줍니다.

 

메모리 정보
Memory Usage 체크박스가 선택되어 있으면(프로파일링 세션을 시작하기 전에 Profile Memory Usage 체크박스를 선택한 경우에만 이 체크박스를 사용할 수 있음) VI에서 어떻게 메모리를 사용하고 있는지에 대한 정보를 볼 수 있습니다. 이 값은 VI의 데이터 공간에서 사용된 메모리를 측정한 것으로 모든 VI에 필요한 지원 데이터 구조는 포함하지 않습니다. VI의 데이터 공간에는 프런트패널 컨트롤에서 명시적으로 사용되고 있는 데이터뿐 아니라 컴파일러에서 암묵적으로 생성한 임시 버퍼도 포함됩니다.메모리 크기는 VI의 실행이 완료될 때 측정되며 정확한 총 사용량을 반영하지 못할 수도 있습니다. 예를 들어, VI가 실행 중에 큰 배열을 생성하였지만 VI가 완료되기 전에 크기를 줄인 경우 표시되는 크기는 중간으로 큰 크기를 반영하지 못합니다.

 

이 섹션에서는 사용된 바이트 수 및 사용된 블록 수와 같이 2가지 데이터 세트를 표시합니다. 블록은 단일 데이터 조각을 저장하는 데 사용되는 인접한 선분입니다. 예를 들어, 정수 배열은 길이는 수 바이트가 될 수 있지만 하나의 블록만 차지합니다. 실행 시스템에서는 배열, 문자열, 경로 및 그림(그림 컨트롤 툴킷에서)에 대해 독립적인 메모리 블록을 사용합니다. 어플리케이션 메모리 힙(heap)의 블록 수가 크면 전체적으로 성능이 저하될 수 있습니다(실행에만 국한되지 않음).

 

메모리 사용의 카테고리는 다음과 같습니다.

  • Average Bytes ‐ 실행당 VI의 데이터 공간에서 사용하는 평균 바이트 수
  • Min Bytes ‐ 개별 실행에서 VI의 데이터 공간에 의해 사용되는 최소 바이트 수
  • Max Bytes ‐ 개별 실행에서 VI의 데이터 공간에 의해 사용되는 최대 바이트 수
  • Average Blocks ‐ 실행당 VI의 데이터 공간에서 사용하는 평균 블록 수
  • Min Blocks ‐ 개별 실행에서 VI의 데이터 공간에 의해 사용되는 최소 블록 수
  • Max Blocks ‐ 개별 실행에서 VI의 데이터 공간에 의해 사용되는 최대 블록 수

 

VI 실행 속도
시간 필수적인 어플리케이션에서 작업할 때 컴파일러에서 일반적으로 빠르게 실행되는 코드를 생성해 주기는 하나 사용자는 VI에서 최상을 성능을 얻기 위해 가능한 모든 일을 하고자 할 것입니다. 이 섹션에서는 실행 속도에 영향을 미치는 원소에 대해 설명하고 최고의 성능을 얻을 수 있도록 도와주는 몇 가지 프로그래밍 기법을 제시합니다.

 

다음 항목을 점검하여 성능이 느려진 원인을 파악해 보십시오.

  • 입/출력(파일, GPIB, 데이터 수집, 네트워킹)
  • 화면 디스플레이(컨트롤이 크지 않은지, 컨트롤이 겹치지 않는지, 디스플레이가 너무 많지는 않은지)
  • 메모리 관리(비효율적인 배열 및 문자열 사용, 비효율적인 데이터 구조)
  • 실행 오버헤드 및 SubVI 호출 오버헤드와 같은 다른 원소도 영향은 미치지만 대부분 그 영향이 매우 적습니다.

 

입출력

입출력(I/O) 호출은 일반적으로 대규모 오버헤드를 야기시킵니다. 종종 입출력 호출은 컴퓨팅 작업보다 시간이 더 많이 걸립니다. 예를 들어, 간단한 직렬 포트 읽기 작업에는 관련된 수 밀리(mili)초의 오버헤드가 있을 수 있습니다. 이 오버헤드는 직렬 포트를 사용하는 모든 어플리케이션에서 발생합니다. 이 오버헤드가 발생하는 이유는 I/O 호출은 운영 체제의 여러 레이어를 거쳐서 정보를 전송해야 하기 때문입니다. 너무 많은 오버헤드를 처리하는 가장 좋은 방법은 I/O 호출 수를 최소화하는 것입니다. 소량의 데이터를 사용하여 여러 번 I/O 호출을 하는 대신에 어플리케이션을 구조화함으로써 각 호출마다 다량의 정보를 전송할 수 있으면 성능이 향상됩니다.

예를 들어, 데이터 수집(NI-DAQ) VI를 생성하는 경우 데이터 읽기에 2가지 옵션을 사용할 수 있습니다. AI Sample Channel VI와 같은 단일 포인트 데이터 전송 함수를 사용하거나 AI Acquire Waveform VI와 같은 멀티포인트 데이터 전송 함수를 사용할 수 있습니다. 100개의 포인트를 수집해야 할 경우 루프에서 Wait 함수와 함께 AI Sample Channel VI를 사용하여 타이밍을 설정합니다. 또한 수집하고자 하는 100 포인트를 입력하여 AI Acquire Waveform VI를 사용할 수도 있습니다.

AI Acquire Waveform VI는 하드웨어 타이머를 사용하여 데이터 샘플링을 관리하므로 이를 통해 보다 빠르고 보다 정확한 데이터 샘플링 속도를 산출할 수 있습니다. 또한 AI Acquire Waveform VI는 더 많은 데이터를 전송하기는 하나 오버헤드는 AI Sample Channel VI에 대한 단일 호출의 오버헤드와 대략 동일합니다.

 

화면 디스플레이

프런트패널의 컨트롤을 빈번히 업데이트하는 것은 어플리케이션에서 가장 시간이 많이 드는 작업 중 하나입니다. 그래프 및 차트와 같은 몇 개의 보다 복잡한 디스플레이를 사용할 경우 특히 그렇습니다. 대부분의 컨트롤은 지능형이기 때문에 이 오버헤드는 일정 수준까지는 최소화됩니다. 새 데이터가 기존의 데이터와 같으면 컨트롤에서 새로운 데이터를 수신해도 다시 그리지 않습니다. 그래프와 차트는 이 규칙의 예외입니다. 다시 그리기 속도가 문제가 되면 최고의 해결책은 사용하는 컨트롤 수를 줄여서 디스플레이를 가능한 간단하게 만드는 것입니다. 그래프 및 차트의 경우 오토스케일링, 스케일 마커 및 그리드를 끄면 디스플레이 속도를 높일 수 있습니다.

다른 객체와 겹치는 컨트롤이 있으면 디스플레이 속도는 더 느려집니다. 속도가 느려지는 이유는 컨트롤이 부분적으로 가려지면 화면에서 가려진 부분에 다시 그리는 데 더 많은 작업을 해야 하기 때문입니다. Smooth Updates 기본 설정을 켜놓지 않으면 컨트롤이 겹칠 때 더 깜박일 수 있습니다.

 

다른 타입의 I/O에서와 같이 컨트롤의 디스플레이에는 고정된 특정 수준의 오버헤드가 있습니다. 차트와 같은 특정 컨트롤을 사용하여 여러 포인트를 한 번에 인디케이터로 전달할 수 있습니다. 매번 차트에 더 많은 데이터를 전달하면 차트 업데이트 횟수를 최소화할 수 있습니다. 수신될 때마다 각 포인트를 표시하는 대신 차트 데이터를 배열로 수집하여 여러 포인트를 한 번에 표시하면 데이터 디스플레이 속도를 훨씬 높일 수 있습니다.

실행 중에 프런트패널이 폐쇄되는 SubVI를 설계할 때 디스플레이 오버헤드에 대해 신경쓰지 마십시오. 프런트패널이 폐쇄되면 컨트롤에 대한 그리기 오버헤드가 없기 때문에 더 이상 그래프가 배열보다 비용이 더 많이 들지 않습니다.

멀티스레드 시스템에서는 컨트롤과 인디케이터에 업데이트의 지연 여부를 제어하는 동기식 디스플레이 팝업 항목이 있습니다. 단일 스레드 실행에서는 이 항목이 아무런 영향력을 가지지 못합니다. 그러나 단일 스레드 버전의 VI에서 이 항목을 켜거나 끄면 해당 VI를 멀티스레드 시스템으로 로드할 경우 변경사항이 업데이트의 동작 방식에 영향을 미치게 됩니다.

 

기본적으로 이 항목은 꺼져 있는데 이는 실행 시스템에서 데이터를 프런트패널 컨트롤 및 인디케이터로 전달한 후에 즉시 실행을 계속할 수 있다는 것을 의미합니다. 그 이후로 일정 포인트에 사용자 인터페이스 시스템은 컨트롤이나 인디케이터에 업데이트가 필요함을 인식하여 새로운 데이터를 표시할 수 있도록 다시 그립니다. 실행 시스템에서 컨트롤을 빠른 연속 속도로 여러 번 업데이트하고자 하면 일부 중재 업데이트는 나타나지 않을 수 있습니다.

대부분 어플리케이션에서는 사용자의 가시 범위에 영향을 미치지 않고도 이렇게 해서 상당 부분 실행 속도를 높일 수 있습니다. 예를 들어, 1초에 불리언 값을 수 백회 업데이트할 수 있는데 이는 육안으로 식별 가능한 업데이트 횟수를 넘어선 것입니다. 비동기식 디스플레이에서는 실행 시스템이 VI를 실행하는 데 더 많은 시간을 할애하는 것이 허용되며 업데이트는 자동으로 사용자 인터페이스 스레드에 의해 느린 속도로 줄어들게 됩니다.
동기식 디스플레이를 원하면 동기식 디스플레이를 켜면 됩니다.

주: 모든 데이터 값을 표시해야 할 필요가 있을 때만 동기식 디스플레이를 켜십시오. 동기식 디스플레이를 사용하면 멀티스레드 시스템에서 성능이 크게 저하됩니다.

 

기타 문제

병렬로 진행되는 블록다이어그램

여러 블록다이어그램이 병렬로 실행되고 있으면 실행 시스템은 블록다이어그램간에 정기적으로 전환됩니다. 이 루프 중 몇몇이 다른 루프보다 덜 중요할 경우 Wait 함수를 사용하여 덜 중요한 루프에서 시간이 덜 소요되도록 해야 합니다.

예를 들어, 다음 블록다이어그램을 살펴봅시다.

 

 
여기에는 병렬로 된 2개의 루프가 있습니다. 한 루프에서는 데이터를 수집하므로 가능한 자주 실행되어야 합니다. 다른 루프에서는 사용자 입력을 모니터링합니다. 루프는 이 프로그램이 구조화된 방식에 따라 동일한 시간을 받게 됩니다. 사용자 동작을 모니터링하는 루프는 일 초에 여러 번 실행될 수 있는 기회가 있습니다.
실제로 대개 버튼을 모니터링하는 루프가 0.5초마다 한번만 실행되거나 그보다 드물게 실행되는 것은 허용됩니다. 사용자 인터페이스 루프에서 Wait(ms) 함수를 호출하면 다른 루프에 훨씬 더 많은 시간을 할당할 수 있습니다.

 

 
 

SubVI 오버헤드

SubVI를 호출할 때 호출과 관련된 일정량의 오버헤드가 있습니다. 이 오버헤드는 매우 작은데(수십 마이크로초의 순서로) 특히 수 밀리 초에서 수 1/10 밀리 초에 이르는 I/O 오버헤드 및 디스플레이 오버헤드와 비교하면 그렇습니다. 그러나 이 오버헤드는 어떤 경우에는 합계될 수 있습니다. 예를 들어, 루프에서 SubVI를 10,000번 호출하면 이 오버헤드는 상당히 많은 시간을 차지할 수 있습니다. 이 경우 SubVI에 루프를 내장할지 여부를 고려해볼 수 있습니다.
또 다른 옵션은 특정 SubVI를 서브루틴으로 전환하는 것입니다(VI 설정 우선 순위 항목 사용). VI가 서브루틴으로 표시되면 실행 시스템에서는 SubVI를 호출하는 오버헤드를 최소화합니다. 그러나 몇 가지 상쇄관계가 있습니다. 서브루틴은 프런트패널 데이터를 표시할 수 없으며(LabVIEW는 서브루틴의 프런트패널 컨트롤에서 데이터를 복사하지 않음) 타이밍 또는 대화상자 기능을 포함할 수 없고 다른 VI와 멀티태스킹을 수행하지 않습니다. 서브루틴은 짧고 빈번히 실행되는 태스크이며 일반적으로 사용자 상호작용이 필요하지 않은 VI와 함께 사용하는 것이 가장 적합합니다.

 

루프에서의 불필요한 계산

계산에서 모든 반복횟수마다 동일한 값이 산출되는 경우 루프에서 계산을 하지 않아도 됩니다. 대신에 루프 밖으로 계산을 이동시켜 결과를 루프로 전달하면 됩니다.
예를 들어, 다음 블록다이어그램을 살펴 봅시다.

 분할 결과가 루프를 통과할 때마다 동일하므로 다음 그림에서와 같이 분할을 루프 밖으로 이동시키면 성능을 향상시킬 수 있습니다. 이제 다음 그림의 블록다이어그램을 참조해 봅시다.

  

이 루프 동안 다른 동시 블록다이어그램이나 VI에 의해 글로벌 변수의 값이 변하지 않는다는 사실을 알고 있으면 이 블록다어이그램은 루프를 통과할 때마다 글로벌 변수를 읽어서 글로벌에 씁니다.이 루프 동안 다른 블록다이어그램에 의해 글로벌 변수를 읽거나 쓸 필요가 없는 경우 다음 블록다이어그램을 대신 사용할 수 있습니다.

 
시프트 레지스터는 SubVI에서 새로운 루프의 반복횟수로 새 값을 전달해야 합니다. 다음 블록다이어그램은 일부 초보 사용자들이 행할 수 있는 일반적인 실수를 보여주고 있습니다. 시프트 레지스터가 없기 때문에 SubVI에서 생긴 결과는 SubVI에 새로운 입력 값으로 다시 전달되지 않습니다.