2012年2月23日木曜日

仮想アプリのパフォーマンス(File書込・読込)について

仮想アプリのパフォーマンス(File書込・読込)について




アプリを仮想化すると、実際にインストールした場合と比べて
どのくらい、パフォーマンスに違いがあるのか、
気になると思いますので、実際にやってみました。
( ThinApp と App-V )

【結果】
インストールと比べて、仮想アプリの速度は、それほど変わらない。
(激しく遅くはない)
※お約束ですが、簡単な計測パターンの結果なのと、マシンやアプリ自体に依存するので
一概に結論付ける事はできません。

 あくまでも、一つの目安にでもしていただけると幸いです。


【計測内容】
250MBのファイルの書込と読込にかかる時間を比較してみます。
(シーケンシャルライトとシーケンシャルリード)
・ネイティブ(物理)
・ThinApp ver4.7.0
・App-V ver4.6 SP1

1回の処理で書込、読込サイズを4パターンで計測してみました。
  1. 4KByte
  2. 32KByte
  3. 512KByte
  4. 1024KByte(1MB)
仮想アプリ製品は、仮想ファイルシステムと物理ファイルシステムに
ファイルの書込、読込が可能です。
書込先が変わることで、パフォーマンスに影響がどのくらいあるかも計測してみました。

各3回計測した、平均時間を計測


【計測環境】
  マシン種別:物理 ノートPC
  OS:Windows 7 SP1 x64
  CPU: Core2Duo
  HDD: 2.5inch 250GB SATA
  メモリ: 2GB
  測定アプリ:自作

  ※計測するために、C言語で簡単なやっつけプログラムを作成してみました。
  実は、このアプリの出来が悪くて、計測結果がフェアじゃないかもしれません。。。
  せっかくの機会なので、書いてみたのでご容赦ください。

【計測結果】

ネイティブ(物理)
ネイティブ
Write(250MB) 1回目 2回目 3回目 平均
4KByte 8.88秒 8.36秒 9.66秒 8.97秒
32KByte 12.12 13.23 14.15 13.17
512KByte 10.72 11.72 12.39 11.61
1MByte 10.92 12.14 12.01 11.69
Read(250MB) 1回目 2回目 3回目 平均
4KByte 10.31 14.21 8.14 10.89
32KByte 7.91 8.60 10.01 8.84
512KByte 7.46 10.78 12.36 10.20
1MByte 6.80 9.70 8.70 8.40

ThinApp ver4.7.0
ThinApp
Mergedモード
ThinApp
WriteCopyモード
Write(250MB) 1回目 2回目 3回目 平均 1回目 2回目 3回目 平均
4KByte 5.60秒 6.30秒 7.33秒 6.41秒 6.41秒 5.69秒 5.52秒 5.87秒
32KByte 11.63 11.64 15.33 12.87 11.03 10.73 12.04 11.27
512KByte 10.81 11.36 13.28 11.82 10.64 11.39 10.48 10.84
1MByte 11.11 11.28 14.06 12.15 11.73 12.00 11.00 11.58
Read(250MB) 1回目 2回目 3回目 平均 1回目 2回目 3回目 平均
4KByte 10.97 9.13 8.53 9.54 9.30 8.22 8.16 8.56
32KByte 7.91 8.22 8.11 8.08 11.17 9.02 8.08 9.42
512KByte 7.82 8.22 8.66 8.23 7.85 8.14 8.02 8.00
1MByte 9.56 8.31 8.30 8.72 7.55 7.32 7.97 7.61

App-V ver4.6 SP1
App-V
マージモード
App-V
上書モード
Write(250MB) 1回目 2回目 3回目 平均 1回目 2回目 3回目 平均
4KByte 10.51秒 21.48秒 11.34秒 14.44秒 6.13秒 8.38秒 14.24秒 9.58秒
32KByte 17.03 22.65 17.45 19.04 20.2212.0911.5014.60
512KByte 11.09 16.10 12.73 13.31 11.0611.6211.3711.35
1MByte 10.00 11.70 10.55 10.75 15.1819.898.6314.57
Read(250MB) 1回目 2回目 3回目 平均 1回目 2回目 3回目 平均
4KByte 8.49 10.09 11.90 10.16 24.752.512.42-
32KByte 7.92 7.91 9.70 8.51 11.390.780.73-
512KByte 9.89 9.19 7.97 9.02 21.650.580.55-
1MByte 9.74 11.26 7.38 9.46 17.000.610.58-
※App-Vの上書きモードの2回目、3回目の読込が激烈に速い。
 メモリにデータをキャッシュしているためと思われる。 あまりにも速いため除外します。
 仮想アプリを終了したタイミングでメモリからディスクへのデータの永続化用の書込が
 行われています。
 そのため、2回目以降のアプリ実行時は、激烈に速くなりますがアプリ全体を終了した
 後にデータの永続化用のディスク書込が激しく行われています。

各製品の書込時間平均
Native ThinApp(Direct) ThinApp(VFS) App-V(Direct) App-V(VFS)
Write(250MB) 物理環境 Merged WriteCopy マージ 上書
4KByte 8.97秒 6.41秒 5.87秒 14.44秒 9.58秒
32KByte 13.17秒 12.87秒 11.27秒 19.04秒 14.60秒
512KByte 11.61秒 11.82秒 10.84秒 13.31秒 11.35秒
1024KByte 11.69秒 12.15秒 11.58秒 10.75秒 14.57秒
※短い方が速い。
4KByteの書込は、なぜかネイティブよりも ThinApp の方が速いです。
1024KByteの書込は、なぜかネイティブよりも App-V の方が速いです。

各製品の読込時間平均
Native ThinApp(Direct) ThinApp(VFS) App-V(Direct) App-V(VFS)
Read(250MB) 物理環境 Merged WriteCopy マージ 上書
4KByte 10.89秒 9.54秒 8.56秒 10.16秒 24.75秒
32KByte 8.84秒 8.08秒 9.42秒 8.51秒 11.39秒
512KByte 10.20秒 8.23秒 8.00秒 9.02秒 21.65秒
1024KByte 8.40秒 8.72秒 7.61秒 9.46秒 17.00秒
※短い方が速い。
4KByte、512KByteの読込は、なぜかネイティブよりも仮想アプリの方が速い事があったり。
App-Vは、1回目は時間が掛かりますが、2回目以降は激烈に速度があがります。

【考察】
測定前の予想としては、ネイティブが全ての項目で、No1.だと思っていました。
また、仮想アプリでは、場合によっては、2倍~4倍程度の速度が遅くなるんじゃないかなと
思っていました。
App-V と ThinApp では、そのアーキテクチャの違いにより、テストケースによっては、
得手不得手がはっきりと現れると思っていました。

が、しかし。
ネイティブよりも仮想アプリの方が何故か速い事があったり
ThinApp と App-V ではそれほど、露骨な違いが無かったり
(App-Vの上書きモードの1回目は遅い傾向が見られましたが。。。)

各製品ともに、かなり成熟されていて、ネイティブとの差がほとんどないように
なっていることがわかりました。
※実は、このアプリの出来が悪くて、計測結果が。。。なのかもしれませんが。。。

少なくとも、私はこれで、より安心して仮想アプリ化をおすすめできそうです。

ただし、App-Vの上書きモードの読込の1回目(OS起動後の初めての実行)の読込は
時間がかかる場合があるので、パフォーマンスが出ない場合は、マージモードに変更して
物理ファイルシステム側にそのファイルを置く対応で何とかなるかと思います。


【測定アプリのコード】
言語:C
コンパイラ:Visual Studio 2010 SP1

#include "stdafx.h"
#include "stdio.h"
#include "time.h"
#include "windows.h"
const char gcszFilePath[]="C:\\FileIOTest\\";
const char gcszFileName4KB[]="FileIOTest4KB.txt";
const char gcszFileName32KB[]="FileIOTest32KB.txt";
const char gcszFileName512KB[]="FileIOTest512KB.txt";
const char gcszFileName1MB[]="FileIOTest1MB.txt";
#define MAX_FILESIZE 1024*1024*250 //250MB
struct fixed4KB_t
{
  char col1[1024*4-2];
  char szCrLf[2];
};
struct fixed32KB_t
{
  char col1[1024*32-2];
  char szCrLf[2];
};
struct fixed512KB_t
{
  char col1[1024*512-2];
  char szCrLf[2];
};
struct fixed1MB_t
{
  char col1[1024*1024-2];
  char szCrLf[2];
};

//--------------------------------------------------------------------------------
void Write4KSeq()
{
 clock_t start={0};
 clock_t end={0};
 start = clock();
 char szFile[MAX_PATH]={0};
 strcpy(szFile,gcszFilePath);
 strcat(szFile,gcszFileName4KB);
 fixed4KB_t *fixed = {0};
 fixed = new fixed4KB_t;
 memset(fixed,'A',sizeof(fixed4KB_t));
 memcpy(fixed->szCrLf,"\r\n",2);


 FILE *fp={0};
 int ret=0;
 for(;;)
 {
  if((fp = fopen(szFile, "wb")) == NULL )
  {
   fprintf(stderr,"ファイルオープンエラー\n");
   break;
  }
  int i=0;
  int iLoopCntMax=0;
  iLoopCntMax=MAX_FILESIZE/sizeof(fixed4KB_t);
  printf("%s 書込 ループ回数 %d ","4KByte",iLoopCntMax);
  for (i=0; i < iLoopCntMax; i++)
  {
   ret = fwrite(fixed, sizeof(fixed4KB_t), 1, fp);
   if(ret !=1)
   {
    fprintf(stderr,"ファイル書込エラー\n");
    break;
   }
  }
  break;
 }
 if(fp)
  fclose(fp);
 if(fixed)
  delete fixed;
 end = clock();
 printf(" %.2f秒かかりました\n",(double)(end-start)/CLOCKS_PER_SEC);
 return;
}
void Read4KSeq()
{
 clock_t start={0};
 clock_t end={0};
 start = clock();
 char szFile[MAX_PATH]={0};
 strcpy(szFile,gcszFilePath);
 strcat(szFile,gcszFileName4KB);
 fixed4KB_t *fixed = {0};
 fixed = new fixed4KB_t;
 memset(fixed,0x00,sizeof(fixed4KB_t));
 FILE *fp={0};
 int ret=0;
 for(;;)
 {
  if((fp = fopen(szFile, "rb")) == NULL )
  {
   fprintf(stderr,"ファイルオープンエラー\n");
   break;
  }
  int i=0;
  int iLoopCntMax=0;
  iLoopCntMax=MAX_FILESIZE/sizeof(fixed4KB_t);
  printf("%s 読込 ループ回数 %d ","4KByte",iLoopCntMax);
  for (i=0; i < iLoopCntMax; i++)
  {
   ret = fread(fixed, sizeof(fixed4KB_t), 1, fp);
   if(ret !=1)
   {
    fprintf(stderr,"ファイル読込エラー\n");
    break;
   }
  }
  break;
 }
 if(fp)
  fclose(fp);
 if(fixed)
  delete fixed;
 end = clock();
 printf(" %.2f秒かかりました\n",(double)(end-start)/CLOCKS_PER_SEC);
 return;
}
//--------------------------------------------------------------------------------
void Write32KSeq()
{
 clock_t start={0};
 clock_t end={0};
 start = clock();
 char szFile[MAX_PATH]={0};
 strcpy(szFile,gcszFilePath);
 strcat(szFile,gcszFileName32KB);
 fixed32KB_t *fixed = {0};
 fixed = new fixed32KB_t;
 memset(fixed,'A',sizeof(fixed32KB_t));
 memcpy(fixed->szCrLf,"\r\n",2);

 FILE *fp={0};
 int ret=0;
 for(;;)
 {
  if((fp = fopen(szFile, "wb")) == NULL )
  {
   fprintf(stderr,"ファイルオープンエラー\n");
   break;
  }
  int i=0;
  int iLoopCntMax=0;
  iLoopCntMax=MAX_FILESIZE/sizeof(fixed32KB_t);
  printf("%s 書込 ループ回数 %d ","32KByte",iLoopCntMax);
  for (i=0; i < iLoopCntMax; i++)
  {
   ret = fwrite(fixed, sizeof(fixed32KB_t), 1, fp);
   if(ret !=1)
   {
    fprintf(stderr,"ファイル書込エラー\n");
    break;
   }
  }
  break;
 }
 if(fp)
  fclose(fp);
 if(fixed)
  delete fixed;
 end = clock();
 printf(" %.2f秒かかりました\n",(double)(end-start)/CLOCKS_PER_SEC);
 return;
}
void Read32KSeq()
{
 clock_t start={0};
 clock_t end={0};
 start = clock();
 char szFile[MAX_PATH]={0};
 strcpy(szFile,gcszFilePath);
 strcat(szFile,gcszFileName32KB);
 fixed32KB_t *fixed = {0};
 fixed = new fixed32KB_t;
 memset(fixed,0x00,sizeof(fixed32KB_t));
 FILE *fp={0};
 int ret=0;
 for(;;)
 {
  if((fp = fopen(szFile, "rb")) == NULL )
  {
   fprintf(stderr,"ファイルオープンエラー\n");
   break;
  }
  int i=0;
  int iLoopCntMax=0;
  iLoopCntMax=MAX_FILESIZE/sizeof(fixed32KB_t);
  printf("%s 読込 ループ回数 %d ","32KByte",iLoopCntMax);
  for (i=0; i < iLoopCntMax; i++)
  {
   ret = fread(fixed, sizeof(fixed32KB_t), 1, fp);
   if(ret !=1)
   {
    fprintf(stderr,"ファイル読込エラー\n");
    break;
   }
  }
  break;
 }
 if(fp)
  fclose(fp);
 if(fixed)
  delete fixed;
 end = clock();
 printf(" %.2f秒かかりました\n",(double)(end-start)/CLOCKS_PER_SEC);
 return;
}

//--------------------------------------------------------------------------------
void Write512KSeq()
{
 clock_t start={0};
 clock_t end={0};
 start = clock();
 char szFile[MAX_PATH]={0};
 strcpy(szFile,gcszFilePath);
 strcat(szFile,gcszFileName512KB);
 fixed512KB_t *fixed = {0};
 fixed = new fixed512KB_t;
 memset(fixed,'A',sizeof(fixed512KB_t));
 memcpy(fixed->szCrLf,"\r\n",2);

 FILE *fp={0};
 int ret=0;
 for(;;)
 {
  if((fp = fopen(szFile, "wb")) == NULL )
  {
   fprintf(stderr,"ファイルオープンエラー\n");
   break;
  }
  int i=0;
  int iLoopCntMax=0;
  iLoopCntMax=MAX_FILESIZE/sizeof(fixed512KB_t);
  printf("%s 書込 ループ回数 %d ","512KByte",iLoopCntMax);
  for (i=0; i < iLoopCntMax; i++)
  {
   ret = fwrite(fixed, sizeof(fixed512KB_t), 1, fp);
   if(ret !=1)
   {
    fprintf(stderr,"ファイル書込エラー\n");
    break;
   }
  }
  break;
 }
 if(fp)
  fclose(fp);
 if(fixed)
  delete fixed;
 end = clock();
 printf(" %.2f秒かかりました\n",(double)(end-start)/CLOCKS_PER_SEC);
 return;
}
void Read512KSeq()
{
 clock_t start={0};
 clock_t end={0};
 start = clock();
 char szFile[MAX_PATH]={0};
 strcpy(szFile,gcszFilePath);
 strcat(szFile,gcszFileName512KB);
 fixed512KB_t *fixed = {0};
 fixed = new fixed512KB_t;
 memset(fixed,0x00,sizeof(fixed512KB_t));

 FILE *fp={0};
 int ret=0;
 for(;;)
 {
  if((fp = fopen(szFile, "rb")) == NULL )
  {
   fprintf(stderr,"ファイルオープンエラー\n");
   break;
  }
  int i=0;
  int iLoopCntMax=0;
  iLoopCntMax=MAX_FILESIZE/sizeof(fixed512KB_t);
  printf("%s 読込 ループ回数 %d ","512KByte",iLoopCntMax);
  for (i=0; i < iLoopCntMax; i++)
  {
   ret = fread(fixed, sizeof(fixed512KB_t), 1, fp);
   if(ret !=1)
   {
    fprintf(stderr,"ファイル読込エラー\n");
    break;
   }
  }
  break;
 }
 if(fp)
  fclose(fp);
 if(fixed)
  delete fixed;
 end = clock();
 printf(" %.2f秒かかりました\n",(double)(end-start)/CLOCKS_PER_SEC);
 return;
}
//--------------------------------------------------------------------------------
void Write1MSeq()
{
 clock_t start={0};
 clock_t end={0};
 start = clock();
 char szFile[MAX_PATH]={0};
 strcpy(szFile,gcszFilePath);
 strcat(szFile,gcszFileName1MB);
 fixed1MB_t *fixed = {0};
 fixed = new fixed1MB_t;
 memset(fixed,'A',sizeof(fixed1MB_t));
 memcpy(fixed->szCrLf,"\r\n",2);

 FILE *fp={0};
 int ret=0;
 for(;;)
 {
  if((fp = fopen(szFile, "wb")) == NULL )
  {
   fprintf(stderr,"ファイルオープンエラー\n");
   break;
  }
  int i=0;
  int iLoopCntMax=0;
  iLoopCntMax=MAX_FILESIZE/sizeof(fixed1MB_t);
  printf("%s 書込 ループ回数 %d ","1MByte",iLoopCntMax);
  for (i=0; i < iLoopCntMax; i++)
  {
   ret = fwrite(fixed, sizeof(fixed1MB_t), 1, fp);
   if(ret !=1)
   {
    fprintf(stderr,"ファイル書込エラー\n");
    break;
   }
  }
  break;
 }
 if(fp)
  fclose(fp);
 if(fixed)
  delete fixed;
 end = clock();
 printf(" %.2f秒かかりました\n",(double)(end-start)/CLOCKS_PER_SEC);
 return;
}
void Read1MSeq()
{
 clock_t start={0};
 clock_t end={0};
 start = clock();
 char szFile[MAX_PATH]={0};
 strcpy(szFile,gcszFilePath);
 strcat(szFile,gcszFileName1MB);
 fixed1MB_t *fixed = {0};
 fixed = new fixed1MB_t;
 memset(fixed,0x00,sizeof(fixed1MB_t));

 FILE *fp={0};
 int ret=0;
 for(;;)
 {
  if((fp = fopen(szFile, "rb")) == NULL )
  {
   fprintf(stderr,"ファイルオープンエラー\n");
   break;
  }
  int i=0;
  int iLoopCntMax=0;
  iLoopCntMax=MAX_FILESIZE/sizeof(fixed1MB_t);
  printf("%s 読込 ループ回数 %d ","1MByte",iLoopCntMax);
  for (i=0; i < iLoopCntMax; i++)
  {
   ret = fread(fixed, sizeof(fixed1MB_t), 1, fp);
   if(ret !=1)
   {
    fprintf(stderr,"ファイル読込エラー\n");
    break;
   }
  }
  break;
 }
 if(fp)
  fclose(fp);
 if(fixed)
  delete fixed;
 end = clock();
 printf(" %.2f秒かかりました\n",(double)(end-start)/CLOCKS_PER_SEC);
 return;
}

int _tmain(int argc, _TCHAR* argv[])
{
 printf("250MBのファイル書込\n");
 Write4KSeq();
 Write32KSeq();
 Write512KSeq();
 Write1MSeq();
 printf("250MBのファイル読込\n");
 Read4KSeq();
 Read32KSeq();
 Read512KSeq();
 Read1MSeq();
 return 0;
}