2009/03/02(月)[SOPC][IP] MMC/SPI FatFs - f_writeベンチマーク

[SOPC][IP] MMC/SPI FatFs - f_writeベンチマーク

[SOPC][IP] MMC/SPI FatFsで紹介しているFatFsについて,コメント欄で問合せいただいたファイル書き込み速度のベンチマークをとってみました.


ベンチマーク概要

FatFsのfopen("wb"相当)で確認する

File systemを使う場合でのベンチマークをとることをテーマとする.*1


脳内シミュレーション(a.k.a.妄想)

FatFs(R0.06)ソースコードより,以下の特徴が推定できる.

  • FatFsはマルチセクタライトもサポートしている模様.
  • セクタ境界・クラスタ境界でクラスタ調整・更新・buffer吐き出し処理を走らせる模様.

上記を踏まえてベンチマークを取得すべきケースは以下のパターンを考えた.

  • クラスタサイズでの一括書き込みが最高スループットを出すだろう
  • クラスタをまたぐ,ランダムアクセスが最低速になる.
  • バッファサイズ(またはセクタサイズ)をまたぐ場合も同様.

ベンチマーク対象とコード

[NiosII][ACM] 動画再生装置(お試し版)の構成で試しているため,キャッシュの影響が結構効いてくることも考えられるので,データ転送量を2Mbytesとします.

実行時間測定には,パフォーマンスカウンタを用います.カウンタを3つとして合成してあるので,測定対象を以下のように絞ります.(なんとなく時間のかかりそうなところ,短縮できそうなところに目星をつけてみたつもりです)

  • 総合時間(規定サイズ・規定回数のブロック転送に要した時間.出力されたファイルの検証は行うべき)
  • SPI転送待ち時間
  • 書き込みデータのCRC計算
ベンチマーク部分本体
const int trans_size[] =
	{
	512*64,
	512*128,
	512,
	4096,
	1024,
	64,
	256,
	768,
	1280,
	1636,
	2048,

	0
	} ;

const int sz_write = 512*2*1024*2 ;

~~~
	while (*sz) {
        f_unlink( fn ) ;    // delete the file before test

        if ((res = f_open (&fp, fn, FA_WRITE|FA_OPEN_ALWAYS|FA_CREATE_ALWAYS)) != FR_OK) {
            return (res | 0x1000);
        }
		// size / loop
		i = 0 ;
		PERF_RESET( PERFORMANCE_COUNTER_BASE );
		PERF_START_MEASURING( PERFORMANCE_COUNTER_BASE );
		PERF_BEGIN( PERFORMANCE_COUNTER_BASE, 1);
		do {
			res = f_write(&fp, sdbuf[ i  ], *sz, &rsz) ;
			if (res != FR_OK || rsz == 0) {
				printf ("Error : Bench-fwrite (%d/%d)\n", res, rsz);
				break;
			}
			i +=  rsz;
		} while (i<sz_write) ;
		PERF_END(PERFORMANCE_COUNTER_BASE, 1);

		PERF_STOP_MEASURING(PERFORMANCE_COUNTER_BASE) ;
		printf( "*** fwrite-size = %d ***\n", *sz );
		perf_print_formatted_report( (void*)PERFORMANCE_COUNTER_BASE, alt_get_cpu_freq(), 3, "total ", "xmit","CRC" );
		WAIT_SEC(5) ;

        f_close(&fp);

		sz++ ;
	}

データ転送部・CRC計算部の処理時間測定位置(mmc.c)
BOOL MMC_xmit_datablock(const BYTE * buff,  /* 512 byte data block to be transmitted */
                    BYTE token  /* Data/Stop token */
    )
{
    BYTE resp, wc = 0;
#ifdef _CALC_CRC
PERF_BEGIN( PERFORMANCE_COUNTER_BASE, 3);
    WORD crc = CRC16((BYTE *) buff, 512);
PERF_END(PERFORMANCE_COUNTER_BASE, 3);
#endif

    if (MMC_wait_ready() != 0xFF)
        return FALSE;

PERF_BEGIN( PERFORMANCE_COUNTER_BASE, 2);
    MMC_xmit_spi(token);            /* Xmit data token */
    if (token != 0xFD) {        /* Is data token */
        do {                    /* Xmit the 512 byte data block to MMC */
            MMC_xmit_spi(*buff++);
            MMC_xmit_spi(*buff++);
        } while (--wc);
#ifdef _CALC_CRC
        MMC_xmit_spi(crc >> 8);     /* CRC */
        MMC_xmit_spi(crc & 0xff);
#else
 @       MMC_xmit_spi(0xFF);         /* CRC (Dummy) */
 @       MMC_xmit_spi(0xFF);
#endif
        resp = MMC_rcvr_spi();      /* Reveive data response */
        if ((resp & 0x1F) != 0x05)      /* If not accepted, return with error */
            return FALSE;
    }
PERF_END(PERFORMANCE_COUNTER_BASE, 2);

    return TRUE;
}

*1 : SDカード本来の性能を最大限に生かすのであれば,マルチセクタライトコマンドを放っておけばよいだろう.

測定結果

使用用したカードは,Transcend mini SDカード 2GBをSDアダプタに挿したものです.

    • セクタあたりのバイト数は,512bytes.
    • クラスタあたりのセクタ数は,32です.
    • データセクタエリアの理想write回数は,4096回,128クラスタを消費します.
    • FATエリアの1セクタあたりのクラスタ数:FAT16ならば1セクタに256個,FAT12なら341個を格納できます.

2MBytesのファイルがクラスタ不連続になっているかどうかの確認をしていません.個人的に使用しているSDカードで,この評価用に使っていないため,テスト用のファイルがクラスタ連続で使用しているかは未確認です.あき暮らす他の探索は先頭からのシーケンシャル探索であり,ファイルのunlink処理では空きクラスタにしているため,パフォーマンスの比較に影響は無いと考えました.


結果のまとめと考察

測定結果と主要項目一覧
転送サイズ総時間512byte書込転送要求回数着目点
655364.03239+712クラスタ,マルチセクタCMD
327684.92526+891クラスタ,マルチセクタCMD
40964.43872+5138セクタ連続書込み,1/8クラスタ,マルチセクタCMD
20484.43872+10254セクタ連続書込み,マルチセクタCMD
1636204.466+12713セクタ連続書込み,100バイトコピー,クラスタ境界をまたぐ,マルチセクタ+シングルセクタCMD
1280135.29+16152セクタ連続書込み,256バイトコピー,マルチセクタ+シングルセクタCMD
10247.80936+20491/32クラスタ,マルチセクタCMD
768199.459+13セクタ連続書込み,クラスタ境界をまたぐ
51213.9297+11/64クラスタ, 1セクタ,シングルライトコマンド
25614.0029+01/2セクタ,シングルライトコマンド
6414.0578+61/8セクタ,シングルライトコマンド
  • セクタ境界をまたがないときは,内部バッファへのコピー処理が走る\→ セクタ数の整数倍ではないモノ全てが対象.\とくに小さいものはコピー処理とループ処理で時間を消費する
  • セクタ境界をまたぐと,書き込み処理が走る.\場合によってはリード処理も走る.
  • DRAM間のコピー処理は効率も悪く,処理時間が延びる傾向にある

考察

転送呼び出し回数と,処理時間とが比例していないのは,MMC側の書き込みバッファがあって,連続した書き込み要求は待たされるが,完結して呼び出す分にはFLASHへの書き込みと,ソフトの走行がパラで走っている状況である,と推察する.

512の整数倍・約数を除く転送サイズでは,軒並み桁違いの遅さが目立つ.メモリコピー処理に加えて,セクタ・クラスタ境界をまたぐ処理が追加されるためと考えらる.加えて,端数はシングルセクタライトコマンドを用いるため,転送効率が悪化すると見られる.

1024byte時に中途半端な速度になっているのは,SD内のFLASH書込み待ち時間が発生しているためと考えられる.

512以下の約数では,不要なコピー処理が走らないが,シングルライトコマンドによるコマンド・レスポンス処理時間がかかるため,カードのbuffered-writeが機能しないのかもしれない.




実行結果ナマデータ

fwrite-size = 32768

Total Time: 4.92526 seconds (487789773 clock-cycles)

Section %Time (sec) Time (clocks)Occurrences
total1004.925264877897671
xmit29.91.471581457432374185
CRC51.52.534742510364714185

fwrite-size = 65536

Total Time: 4.03239 seconds (399361828 clock-cycles)

Section %Time (sec) Time (clocks)Occurrences
total1004.032393993618221
xmit36.31.465131451044754167
CRC62.52.518662494441814167

fwrite-size = 512

Total Time: 13.9297 seconds (1379575366 clock-cycles)

Section %Time (sec) Time (clocks)Occurrences
total10013.9296913795753601
xmit10.51.457821443799864097
CRC19.72.741222714866154097

fwrite-size = 4096

Total Time: 4.43872 seconds (439603921 clock-cycles)

Section %Time (sec) Time (clocks)Occurrences
total1004.438724396039151
xmit331.463601449530084609
CRC64.32.854522827077134609

fwrite-size = 1024

Total Time: 7.80936 seconds (773427070 clock-cycles)

Section %Time (sec) Time (clocks)Occurrences
total1007.809367734270641
xmit18.71.461001446947596145
CRC50.83.966453928315936145

fwrite-size = 64

Total Time: 14.0578 seconds (1392260079 clock-cycles)

Section %Time (sec) Time (clocks)Occurrences
total10014.0577713922600731
xmit10.41.465021450932744102
CRC20.12.831102803873134102

fwrite-size = 256

Total Time: 14.0029 seconds (1386825298 clock-cycles)

Section %Time (sec) Time (clocks)Occurrences
total10014.0029013868252921
xmit10.41.462951448886934096
CRC202.801532774594664096

fwrite-size = 768

Total Time: 199.459 seconds (19754119034 clock-cycles)

Section %Time (sec) Time (clocks)Occurrences
total100199.45907197541190281
xmit0.7321.460151446106294097
CRC1.32.594872569914484097

fwrite-size = 1280

Total Time: 135.29 seconds (13398914257 clock-cycles)

Section %Time (sec) Time (clocks)Occurrences
total100135.29001133989142511
xmit1.081.461521447470515711
CRC2.683.623653588810705711

fwrite-size = 1636

Total Time: 204.466 seconds (20250035225 clock-cycles)

Section %Time (sec) Time (clocks)Occurrences
total100204.46638202500352191
xmit0.7181.467311453200055367
CRC1.663.392833360204435367

fwrite-size = 2048

Total Time: 4.88632 seconds (483933442 clock-cycles)

Section %Time (sec) Time (clocks)Occurrences
total1004.886324839334361
xmit301.463841449761165121
CRC65.33.190863160180685121


今後の課題

SD側の応答待ち時間の計測,f_write内の分岐処理回数を付け加えれば,想像の裏づけを取ることもできるだろう.

カードの性能にも左右されるベンチマークであるため,必ずしも上記の結果どおりとはならないでしょう.カード性能を測定するベンチマークハードウェアを作るのも面白いかもしれませんね.File systemを無視して,SDのwrite buffer sizeや応答時間なんかの測定を自動的にやってくれるのがあると,実力チェックに使えて面白そうです.

実使用に耐えるかどうかを判断するためには,file system経由での(sequential|random)(read|write)も必要でしょう.

まぁ,あくまでもMMCのベンチマークということで... :p