アプリを仮想化すると、実際にインストールした場合と比べて
どのくらい、パフォーマンスに違いがあるのか、気になると思いますので、実際にやってみました。
( ThinApp と App-V )
【結果】
インストールと比べて、仮想アプリの速度は、それほど変わらない。
(激しく遅くはない)
※お約束ですが、簡単な計測パターンの結果なのと、マシンやアプリ自体に依存するので
一概に結論付ける事はできません。
あくまでも、一つの目安にでもしていただけると幸いです。
【計測内容】
250MBのファイルの書込と読込にかかる時間を比較してみます。
(シーケンシャルライトとシーケンシャルリード)
・ネイティブ(物理)
・ThinApp ver4.7.0
・App-V ver4.6 SP1
1回の処理で書込、読込サイズを4パターンで計測してみました。
- 4KByte
- 32KByte
- 512KByte
- 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.22 | 12.09 | 11.50 | 14.60 |
| 512KByte | 11.09 | 16.10 | 12.73 | 13.31 | 11.06 | 11.62 | 11.37 | 11.35 |
| 1MByte | 10.00 | 11.70 | 10.55 | 10.75 | 15.18 | 19.89 | 8.63 | 14.57 |
| Read(250MB) | 1回目 | 2回目 | 3回目 | 平均 | 1回目 | 2回目 | 3回目 | 平均 |
| 4KByte | 8.49 | 10.09 | 11.90 | 10.16 | 24.75 | 2.51 | 2.42 | - |
| 32KByte | 7.92 | 7.91 | 9.70 | 8.51 | 11.39 | 0.78 | 0.73 | - |
| 512KByte | 9.89 | 9.19 | 7.97 | 9.02 | 21.65 | 0.58 | 0.55 | - |
| 1MByte | 9.74 | 11.26 | 7.38 | 9.46 | 17.00 | 0.61 | 0.58 | - |
メモリにデータをキャッシュしているためと思われる。 あまりにも速いため除外します。
仮想アプリを終了したタイミングでメモリからディスクへのデータの永続化用の書込が
行われています。
そのため、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;
}


