Virtio Block 성능 세부 분석

컴퓨터공부/가상화기술 2019.05.20 20:28 Posted by 아는 개발자 아는 개발자

예전 포스트에서는 iozone을 이용해 Virtio block 드라이버의 성능을 간단하게 측정해봤다면  이번 글에는 범용적으로 사용되는 스토리지 벤치마크인 fio를 이용해 Virtio Block의 성능을 좀더 디테일하게 분석해보려고 한다.


실험의 큰 단위를 Sequential, Random으로 나누고 각각의 I/O size를 바꿔봤을 때 Host와 VM의 성능 차이가 어느 정도 나오는지를 분석 해봤다.


1. Seq 512K, Rand 4K


 

 Host

VM 

Ratio 

Random Read 

7705MB/s 

5184MB/s 

67% 

Random Write 

120MB/s 

69.9MB/s 

58% 

Sequential Read 

13.5GB/s 

12.4GB/s 

91% 

Sequential Write 

501MB/s 

346MB/s 

69% 


Sequential에는 512K의 io size를, Random인 경우에는 4K 단위로 io size를 줬다. 이 값들은 실제 스토리지 벤치마크에서 자주 사용되는 단위다. 


결과 값의 편차가 크기는 하지만 Sequential의 경우가 Random의 경우보다 성능이 더 잘 나오는 것으로 보인다. 그런데 random이 sequential 대비 성능이 이렇게 떨어지는 것은 이상해 보인다. 읽는 과정은 크게 차이가 없어 보이는데 말이다.


2. Random 512K 


 

 Host

VM 

Ratio 

 Random Read

13.3GB/s 

12.5GB/s 

93% 

 Random Write

524MB/s 

366MB/s 

70% 


Random Access가 원인이기 보다는 I/O size의 크기에 따라 성능이 달라지는 것 같아서 Random의 경우에도 Sequential과 동일하게 I/O 사이즈를 512K로 줬다. 그러자 Sequential 때와 거의 비슷한 수준으로 성능이 나타났다. I/O 사이즈에 따라서 성능이 들쭉날쭉한다. 


3. Virtio Block Process Sequence




간략하게 표현하면 Virtio Block 드라이버의 처리 루틴은 위와 같은 방식으로 이뤄진다. 실제 스토리지 장치에 접근하는 Block Driver는 Native와 똑같은 장치를 사용하고 있으니 이 지점에서는 Native와 Virtual Machine 모두 똑같은 원리로 동작해 성능 차이가 나지 않는다. 오버헤드가 발생할 수 있는 부분은 이 지점 이외의 부분, VM에서 Block 드라이버까지 요청이 전달 되는 화살표로 표현한 과정에서 발생한다. 화살표 요청의 횟수가 적을수록 Native에 비슷한 성능을 내고 많을수록 저조한 성능을 보인다.


4. I/O operations between 4K random and 512K random 


QEMU의 trace 기능을 이용해 4K일 때와 512K일 때 Virtio Block 함수를 호출하는 횟수를 측정 해봤다.


 

 4K

512K 

 4K / 512K

 Random Read

 279727

25733 

 10.87 : 1

 Random Write 

 3920485

141694 

 27.6687


Read의 경우에는 4K의 경우가 512K보다 10배 정도 더 Virtio Block 관련 함수를 호출하고 Write의 경우에는 27배 더 호출하고 있다. 호출 횟수와 실제 성능과 비교해보면 Random Read의 Host 대비 성능(67%)이 Random Write의 결과값(58%)보다 높게 나온 것으로 보아 Virtio Block 함수 호출 횟수와 어느정도 비례하고 있는 것을 알 수 있다. 


'컴퓨터공부 > 가상화기술' 카테고리의 다른 글

Virtio Block 성능 세부 분석  (0) 2019.05.20
Virtio Block 성능 벤치마크  (0) 2018.12.23
QEMU를 이용해 커널 이미지 바꿔서 부팅해보기  (0) 2018.12.20
kvm ioeventfd  (0) 2018.08.11
kvm irqfd  (0) 2018.08.11
vhost  (0) 2018.07.08
TAG Block, FIO, virtio

SDN과 NFV

컴퓨터공부/클라우드컴퓨팅 2019.04.21 13:28 Posted by 아는 개발자 아는 개발자

SDN (Software Defined Network)

SDN은 "네트워크 자원을 소프트웨어적으로 가상화해서 운영하는 기술이다" 라고 한줄만 가지고 이해하기 모호하니 딱 정의하기에 앞서 이 기술이 나오게 된 배경부터 주목해보자. 지금과 같이 인터넷이 크게 발달하기 전에는 트래픽의 양이 많지 않았고 트래픽의 패턴도 서버와 클라이언트에게 주고받는 데이터 정도로 단순했다. 그런데 스마트폰의 시대가 시작되면서 트래픽의 양이 급격히 증가하기 시작했고 패턴도 단순히 클라이언트와 주고 받는 것이 아니라 다른 서버의 데이터베이스에 접근하는 경우가 생기기 시작했다. 이전보다 네트워크 관리 하는 것이 훨씬 복잡해졌다.

과거에는 네트워크 장비들이 모두 하드웨어로만 조작 할 수 있었다. 트래픽이 감당 할 수 없을 정도로 많아진 경우에는 서버를 더 사야했고 다른 서버로 연결 할 때는 연결시 필요한 방화벽, 스위치 등등도 추가로 구매해야 했다. 이런 경우들은 비용문제도 있을 뿐만 아니라 기존 서버에 새로운 하드웨어를 증설하는 일이 꽤나 시간도 오래 걸리고 번거롭다는 문제가 있었다. 가상화 기술이 나오면서 네트워크 장치들도 소프트웨어적으로 해결해 관리를 편하게 하려는 움직임이 있었는데 이런 요구사항으로 나오게 된 것이 SDN이다. 


출처: 주니퍼

SDN을 사용하면 서버를 여러대를 살 필요 없이 한번에 성능 좋은 서버를 산후 여러개의 VM을 돌려서 복수의 서비스를 돌릴 수 있다. 특정 서비스에 트래픽이 몰리면 이전처럼 서버를 추가로 증설할 필요 없이 트래픽이 몰리는 VM에 하드웨어 자원을 더 많이 할당해서 해결 할 수 있다. 예전처럼 하드웨어적으로 처리할 필요가 없어 관리하기가 쉬우며 놀고 있는 다른 VM의 자원들을 끌어다가 사용할 수 있기 때문에 비용적으로도 효율적이다.


NFV (Network Function Virtualization)

NFV는 앞서 설명한 SDN을 소프트웨어적으로 구현할 수 있는 기술이다. 구체적으로 설명하면 switch, firewall 같은 표준화된 네트워크 장비들을 소프트웨어적으로 만들수 있으며 서버에 설치된 네트워크, 스토리지, CPU 등 컴퓨팅 리소스의 orchestration을 담당하는 컴포넌트다. SDN이 NFV를 포함하는 기술이라고 보면 될 것 같다. 사실은 거의 전부라고 생각되긴 하지만.

출처: 주니퍼

세세하게 들어가면 끝도 없으니 위 그림의 컴포넌트들에 대해서 짤막하게만 알아두고 가자.

VNF (Virtualized Network Functions)

가상화 해서 만든 네트워크 장비들이다. 가상의 방화벽, 스위치, 로드밸런서 등등이 이에 해당한다. 필요에 따라서 여러 개를 만들 수 있다. 클라우드 환경에서 돌아가고 있는 서비스들이 바라보는 네트워크 장비이기도 하다.

NFVI (NFV Infrastructure)

앞서 설명한 VNF와 실제 하드웨어 장비간의 인터페이스를 담당한다. Virtualization Layer를 통해서 물리 장비에 매핑 할 수 있는 컴포넌트다. API라고 생각하면 좋을 것 같다.

NFV MANO(Management and Orchestration)

가상의 네트워크 장비(VNF)를 NFV 인프라스트럭처 (NFVI)를 이용해 실제 하드웨어에 매핑하고 NFV에서 자원 할당 문제로 번번히 언급되는 VNF 스케줄링, VNF 연결문제(Chain Composition) 등등을 다루는 핵심 컴포넌트다. 

'컴퓨터공부 > 클라우드컴퓨팅' 카테고리의 다른 글

SDN과 NFV  (0) 2019.04.21
Kubernetes 소개  (0) 2018.06.23
오픈스택 구조 분석  (0) 2017.03.07
오픈스택이란?  (0) 2017.03.07
클라우드 가상화와 Docker  (0) 2017.03.05
클라우드 컴퓨팅(Cloud Computing)  (0) 2017.02.25
TAG NFV, SDN

JAVA의 static

컴퓨터공부/안드로이드 2019.04.03 21:50 Posted by 아는 개발자 아는 개발자

0. JAVA Static

절차 지향인 C언어에서 static 변수는 함수 또는 변수를 작성 중인 파일 내에서만 사용하고 싶을 때만 사용하기 때문에 사용법이 비교적 간단했는데 JAVA에서는 객체지향 개념과 연관되어 있어 사용 시 다소 주의가 필요하다. 이번 포스트에서는 JAVA 언어에서 static의 쓰임새에 대해서 정리를 해본다.


1. Static/Non-static 멤버

자바에서는 클래스 내의 함수 또는 변수를 멤버(Member)라고 통칭해서 부르며 앞에 static 변수를 붙이면 static 멤버, 붙이지 않으면 Non-static 멤버로 분류한다.

public class MainActivity extends AppCompatActivity {
  static int staticVar = 0;       // static Member
  static void staticMethod() {}   // static Member

  int nonStaticVar = 0;           // Non-static Member
  void nonStaticMethod() {}       // Non-static Member
}

Static 멤버는 별도의 객체 생성과정 없이 바로 사용할 수 있고 클래스당 하나씩만 존재해 동일한 클래스로 생성된 복수의 객체가 동일한 변수의 값을 공유하게 된다. C/C++의 전역 변수와 함수와 원리가 비슷한데 이와 같은 특징은 Static 멤버들이 객체를 생성하기 전에 이미 메모리에 로딩되며 객체가 제거와 상관없이 프로그램이 종료될 때까지 메모리에 남아있는 구조적인 특징 때문에 발생한다.

Non-Static 멤버는 Static 멤버와 원리가 정반대다. 반드시 객체를 생성한 후에 사용할 수 있으며 생성된 객체별로 멤버의 값이 다를 수 있다. 이는 멤버 인스턴스들이 객체의 생명주기에 따라서 메모리에 생성되고 제거되는 원리 때문이다. Non-Static 멤버들은 객체지향적인 관점에 근거해서 동작하는 인스턴스다.


2. 사용 예시

2.1 Singleton Pattern

프로그램에서 클래스당 하나의 객체만 사용하도록 유도하는 Singleton Pattern은 static을 적절히 사용한 대표적인 예다. getInstance 함수를 별도의 객체 생서 없이 호출할 수 있도록 static 멤버로 선언했으며 클래스 변수인 instance도 static으로 선언해 여러개의 객체가 생성되더라도 동일한 객체만 리턴할 수 있도록 했다.

public class Singleton {
  private static Singleton instance;
  private LazySingleton() {}
  public static synchronized Singleton getInstance() {
      if(instance == null) {
          instance = new Singleton();
      }
      return instance;
  }
}

2.2 Math

private int getAbs(int a) {
	return Math.abs(a);
}

별다른 객체 생성 없이 바로 실행하는 Math 클래스의 함수들은 모두 static 멤버들로 선언된 것들이다. JAVA는 객체지향 관점에 따라서 모든 함수들은 반드시 객체를 통해서 호출되기 때문에 위와 같이 static 함수를 적극적으로 활용해 라이브러리를 호출할 수 있도록 했다.


3. 주의사항

3.1 Out of Memory

객체가 제거되도 static 멤버는 메모리에 남아있기 때문에 코드 상에서 별도로 처리해두지 않으면 모든 메모리 공간을 static 인스턴스가 차지하는 메모리 Leak이 발생할 수 있다.

public class OomClass {
  static Vector oomVector;
  static void addOomVector(int a) {
      oomVector.add(a);
  }
  public OomClass() {
      for (int i = 0; i < 100000; i++)
          addOomVector(i);
  }
}

위의 OomClass는 생성 할 때마다 static 변수는 oomVector 벡터 클래스에 0~99999까지 엔트리를 추가하는데 OomClass 한 두 개 정도 생성할 때는 별 문제가 되지 않지만 그 수가 많아지면 메모리에서 차지하는 비율이 높아져 OOM이 발생할 수 있다.

3.2 Static 함수의 제약사항

Static 함수 내에서는 Static 멤버만 사용할 수 있고 this 호출이 불가능하다. 이런 제약사항은 역으로 Non-Static 멤버도 접근 할 수 있고 this를 호출 할 경우 어떤 문제점이 발생할지 예측해보면 이해하기 쉽다. Non-Static 멤버들은 객체가 생성되어야 메모리에 한자리 차지하게 되는데 객체 생성 없이 바로 호출되는 static 함수에서 생성 됐는지 확인되지 않는 멤버 변수들에게 접근한다면 이는 invalid access가 된다. 구조적인 특징에 대해서 다시 한번 생각해보게 된다.



'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

안드로이드 Loader  (0) 2019.07.15
onSaveInstanceState  (0) 2019.07.15
JAVA의 static  (0) 2019.04.03
안드로이드 Service  (0) 2019.03.19
AsyncTask  (0) 2019.03.13
ViewPager와 PageAdapter  (0) 2019.03.05

안드로이드 Service

컴퓨터공부/안드로이드 2019.03.19 21:42 Posted by 아는 개발자 아는 개발자

0. 소개


Service 클래스는 안드로이드 앱에서 백그라운드 작업을 수행할 때 대표적으로 사용하는 라이브러리다. 앞서 설명한 AsyncTask와는 사용법이 완전히 다른데 2~3초 이내로 짧게 끝내야하는 AsyncTask와는 달리 Service는 오래도록 실행 할 수 있으며 AsyncTask처럼 UI 쓰레드와 Interaction 할 수 있는 기능은 없다. 그래서 화면과 무관한 작업인 네트워크 트랜잭션 처리나 음악을 재생할 때 사용된다.


1. 생성주기


액티비티와 독립된 생성주기를 가지고 있으며 별도의 컴포넌트로 동작하기 때문에 애플리케이션을 사용하지 않은 상태(UI가 화면에 있지 않은 상태) 여도 백그라운드에서 실행 될 수 있다. 폰 게임을 하는 중에도 카카오톡 메시지가 오고 쿠팡에서 쇼핑을 하는 도중에도 멜롬의 음악을 들을 수 있는 이유가 바로 이런 서비스 클래스의 특징 덕분이다. 경우에 따라선 애플리케이션 내에서 별도의 프로세스로 분리할 수도 있다.


2. 콜백함수 


Service 클래스의 콜백 함수를 통해 간단히 사용 방법을 익혀보자.


public void onCreate()


서비스가 생성 될 때 가장 먼저 실행되는 함수. 생성시 딱 한번만 호출되며 서비스 동작에 필요한 인스턴스를 선언하는 작업으로 주로 사용 한다.


public void onDestroy()


서비스가 종료될 때 호출되는 함수. onCreate에서 수행한 작업과 반대로 서비스가 종료하면서 인스턴스에 정리할 작업을 처리하는 일을 한다.


public int onStartCommand(Intent intent, int flags, int startId)


사용자의 요청을 처리하는 함수. 서비스를 처음 생성할 경우에는 onCreate 함수 다음에 불리나 생성 이후에는 이 함수로 바로 불린다. 전달 인자인 Intent에 필요한 작업을 전달할 수 있다.


3. 주의사항


단 주의할 것이 무한루프처럼 오래 걸리는 작업을 onStartCommand에 넣으면 ANR(Application Not Responding)에러가 발생하게 된다. 오래 실행하는 작업을 수행할 때 사용되는 라이브러린데 정작 사용하려니 에러가 발생하는게 생뚱맞게 들릴 수 있는데 이는 서비스 클래스 컴포넌트의 구조적인 이유 때문이다. onStartCommand함수는 서비스 작업 뿐만 아니라 사용자 interaction도 처리하는 메인쓰레드에서 담당하는데 이 함수가 빨리 종료되지 않는다면 사용자는 UI 작업을 처리하지 못하게 된다.


이러한 이유 때문에 오래 걸리는 작업은 onStartCommand에서 처리하되, 아래의 안드로이드 소스처럼 별도의 쓰레드를 생성해서 작업을 처리하도록 한다.


 
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    new Thread() {
        @Override
        public void run() {
            while (true) {
                try {
                    sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }
    }.start();

    return START_STICKY;
}


4. IntentService 


매번 쓰레드를 만들어서 작업을 처리한다면 여러 가지 작업을 동시에 처리할 수 있다는 이점도 있지만 개수가 많아짐에 따라 시스템 자원을 소모하게 되고 경우에 따라선 처리 시간은 중요하지 않고 작업이 순차적으로 처리하는 일이 필요한 경우 불필요한 오버헤드를 발생시키기도 한다. 차라리 이런 경우에는 여러 개의 쓰레드를 만들지 말고 하나의 쓰레드에서 작업을 하나씩 전달해주는 방식이 좋을 것 같기도 하다. 안드로이드에서는 이미 이런 경우를 고려해 IntentService 라는 클래스를 만들어 뒀다.


IntentService를 사용하면 메인 thread와 따로 도는 workqueue쓰레드가 생성되며 액티비티로부터 전달 받은 Intent를 순차적으로 아래의 onHandleIntent 함수에서 처리한다. 


protected void onHandleIntent(Intent intent)


onStartCommand 함수에서는 긴 작업을 별도의 쓰레드에서 처리했어야 했다면 IntentService의 onHandleIntent 함수안에서는 바로 길게 실행되는 작업을 둬도 상관 없다. 안드로이드가 알아서 작업을 처리해준다.

'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

onSaveInstanceState  (0) 2019.07.15
JAVA의 static  (0) 2019.04.03
안드로이드 Service  (0) 2019.03.19
AsyncTask  (0) 2019.03.13
ViewPager와 PageAdapter  (0) 2019.03.05
px, dp, sp 개념 정리  (0) 2019.02.16

AsyncTask

컴퓨터공부/안드로이드 2019.03.13 23:57 Posted by 아는 개발자 아는 개발자

0. 소개


AsyncTask는 파일 다운로드 완료후 사용자에게 완료됐다는 Toast 메시지를 보내는 유스케이스처럼 특정 백그라운드 작업이 종료되고 사용자에게 화면으로 알림을 전달해야하는 경우 유용한 클래스다. 코드가 직관적이고 응용하기 쉬워 자주 사용되는 라이브러리지만 몇초 이내에 종료될 수 있는 작업인 경우에만 사용이 가능하며 그것보다 긴 작업은 다른 클래스를 사용해야 한다.


안드로이드 공식문서의 예제 코드를 통해 사용 방법을 익혀보자.


private class DownloadFilesTask extends AsyncTask<URL, Integer, Long> {
     protected void onPreExecute(Integer... progress) {
         /* do nothing */
     }
     protected Long doInBackground(URL... urls) {
         int count = urls.length;
         long totalSize = 0;
         for (int i = 0; i < count; i++) {
             totalSize += Downloader.downloadFile(urls[i]);
             publishProgress((int) ((i / (float) count) * 100));
             // Escape early if cancel() is called
             if (isCancelled()) break;
         }
         return totalSize;
     }
     protected void onProgressUpdate(Integer... progress) {
         setProgressPercent(progress[0]);
     }
     protected void onPostExecute(Long result) {
         showDialog("Downloaded " + result + " bytes");
     }
 }



1. 상속 함수


클래스의 작업은 진행 순으로 크게 네가지로 구분된다.


protected void onPreExecute


이름에서 직감할 수 있듯이 AsyncTask 실행시 가장 먼저 불리는 함수다. 주로 여기에 백그라운드를 시작하기 전에 초기화해야하는 변수나 객체를 선언하는 용도로 사용한다.


protected Long doInBackground


백그라운드 작업이 실행되는 함수다. 예제 코드에서 확인 할 수 있듯이 여러 개의 인자를 전달받을 수 있으며 작업이 종료되면 특정 자료형의 결과 값을 리턴한다. 파라미터와 결과의 타입을 설정하는 방법은 다음 챕터에서 설명할 예정이다.


protected void onPostExecute

 

doInBackground  함수가 종료된 후 실행되는 함수다. doInBackground 함수의 결과값을 파라미터로 받으며 주로 예제 코드처럼 실행 완료메시지를 사용자에게 알림하는데 사용한다.


protected void onProgressUpdate


doInBackground 내에서 publishProgress 함수를 호출 할 때 불리는 함수이며 주로 작업의 진행 상황을 표시하는데 사용된다. 예제 코드에서는 다운로드 완료를 퍼센테이지로 전달했다.  



2. AsyncTask 인자


예제 코드의 DownloadFilesTask 클래스는 AsyncTask<URL, Integer, Long>을 상속하는데 여기서 부모클래스의 template 인자의 타입은 순서에 따라서 앞서 설명한 네가지 작업의 파라미터의 타입으로 매핑된다. 


첫번째 인자 (URL)


백그라운드로 실행하는 작업인 doInBackground 함수의 파라미터 타입이다. 주로 처리해야할 작업의 데이터를 전달하는 용도로 사용한다. 예제 코드에서는 URL 타입으로 설정해서 다운로드 받을 파일의 주소를 전달하는 용도로 사용하고 있다.


두번째 인자 (Integer)


진행 상황을 업데이트하는 onProgressUpdate 함수의 파라미터 타입이다. 예제 코드의 doInBackground 함수에서는 다운로드 진행 상황을 publishProgress 함수로 퍼센트로 전달하고 있으며 onProgressUpdate 함수는 Integer 형태로 받아서 화면에 표시하고 있다.


세번째 인자 (Long)


백그라운드 작업 종료후 실행하는 onPostExecute 함수의 파라미터 타입이다. doInBackground 함수의 리턴값이 onPostExecute 함수의 인자 값으로 전달되며 주로 작업의 정상종료 유무를 전달하기 위해 사용한다.



'컴퓨터공부 > 안드로이드' 카테고리의 다른 글

JAVA의 static  (0) 2019.04.03
안드로이드 Service  (0) 2019.03.19
AsyncTask  (0) 2019.03.13
ViewPager와 PageAdapter  (0) 2019.03.05
px, dp, sp 개념 정리  (0) 2019.02.16
Device screen dpi 값에 따라 처리하기  (0) 2019.02.16