hungalab / cube_sim

Cube System Simulator based on VMIPS

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

キャッシュエミュレーションの実装

tkojima0107 opened this issue · comments

オリジナルの実装ではキャッシュらしきものはあるがdirect mapでしかも、ブロックサイズ=1のキャッシュと呼んで良いのかわからない代物。加えてライトスルー方式。

キャッシュミスペナルティも考慮したn-way associative cache化したい。

順番としては

  1. キャッシュとしての振る舞いをエミュレーション
  2. キャッシュミス時のstallを実装
    という手順で進めていく。

ただし、キャッシュミス時のstallを実装するには少なくとも以下を考慮する必要がある。

  • 5ステージパイプラインでは命令キャッシュとデータキャッシュでミスするステージが異なる
    • 命令キャッシュはFetchスレージ
    • データキャッシュはMEMスレージ
    • 当然同時にミスすることも考慮しなくてはいけない
  • 遅延スロットの取り扱い
    • VMIPSでは厳密にパイプラインをエミュレーションできてない
      • 多くはEXEステージを毎サイクル実行?

あとMIPS R3000ってデータハザードでパイプラインの停止とかインターロックって実装されてたのか忘れたから調べる

#2 を実装することを考えるとキャッシュミス時に サイクル時間 += ペナルティ とするのではダメ
理由は以下の2つ

  • キャッシュミスによるストール時にもアクセラレータは動いていなくてはいけない
  • キャッシュフェッチするデータがTCIを経由したアクセラレータ内データの場合がある(TCIをちゃんとエミュレーションするならこのフェッチにかかる時間は予測不可)

16760a5 でn-way キャッシュの動作エミュレーションはできた。あとは、サイクル数をエミュレートする

8dfecc1 にてキャッシュをmapperから分離できた。
また、もともとあるDeviceExcクラスがCPUの親クラスだったので、これに

void stall(int cause);

をプロトタイプとして組み込んでみた。実際に使うとなるとこれではダメなきがするから拡張する可能性あり。

やること

  • Cache::stepの実装(cache fetch/cache write back)
  • mapper::readyを使ってstallをエミュレーション
  • @zoubleton
    • mapperにアービトレーション
      • Mapper::ready()で相手に通知する (rangeインスタンスのreadyも考慮)
      • アクセスの初期遅延をMapper::step()で制御
        到達遅延はmapperで制御、計算による遅延はrouterで制御
    • DMACの実装

stallなしでjpegした場合の結果

2757827 instructions in 0.58308 seconds (4729798.053 instructions per second) (stall ratio 1.031%) 
Instruction Cache Profile 
	Cache Miss Ratio 0.04891% 
	write back ratio 0.00000% 
Data Cache Profile 
	Cache Miss Ratio 6.48642% 
	write back ratio 34.27902% 

やること再確認

メモリアクセスのインターフェース

  1. Bool ready(uint32 addr, int32 mode, DeviceExc *client);
    counterが0かつrange側のreadyがたったらtrueを返す
  2. void requstWord(uint32 addr, int32 mode, DeviceExc *client);
    mapperにエントリがない場合counterを初期化、それ以外は無視 -> cpu,cache側からいくら読んでも良い
  3. uint32 fetch_word(uint32 addr, int32 mode, DeviceExc *client);
    mapperのエントリを削除する
    fetch_halfやstore_wordなども同様

前提

  • cpuとキャッシュから同時にrequestが来ないようにする
  • キャッシュのバーストアクセスを再現するには初めに全ライン分requestを飛ばし、一斉にcounterをdecrementしてもらう

キャッシュミスを考慮した結果

3751539 instructions in 0.53646 seconds (6993151.387 instructions per second) (stall ratio 27.246%)
Instruction Cache Profile
	Access Count 2730713
	Cache Miss Ratio 0.04889%
	write back ratio 0.00000%
Data Cache Profile
	Access Count 742993
	Cache Miss Ratio 6.09131%
	write back ratio 34.27902%

所用サイクル数が1.36倍になった。
フェッチミスの場合余分に15サイクル
データミスでライトバックありの場合31サイクル, それ以外は15サイクル余分にかかるから
フェッチのペナルティ: 2730713 * 0.00048 * 15 = 1,311
データアクセスのペナルティ: 742993 * 0.0609 * ((1 - 0.342) * 15 + 0.342 * 31) = 926,322
(2757827 + 1311 + 926322) / 2757827 = 1.33
妥当そう

ちなみにGeyser RTLシミュレーションでは

236725700: end successfully
Simulation complete via $finish(1) at time 236925700 NS + 0
../forTest/testamode.v:166          $finish;
ncsim> exit

real    1m14.382s
user    1m3.892s
sys     0m0.115s

1 cycle = 50nsなので4,734,514サイクル
オーダーは悪くない

シミュレーションにかかる時間は150倍くらい

ひとまずできたが遅い

 %   cumulative   self              self     total           
 time   seconds   seconds    calls  us/call  us/call  name    
 99.14     87.89    87.89  3993040    22.01    22.01  Mapper::step()
  0.08     87.96     0.07  3993040     0.02     0.02  CPU::pre_decode(bool&)
  0.07     88.02     0.06  8041079     0.01     0.01  Cache::cache_hit()
  0.07     88.08     0.06  5981261     0.01     0.02  std::_Hashtable<Mapper::RequestsKey, std::pair<Mapper::RequestsKey const, int>, std::allocator<std
::pair<Mapper::RequestsKey const, int> >, std::__detail::_Select1st, Mapper::RequestsKeyEqual, Mapper::RequestsHash, std::__detail::_Mod_range_hashing, 
std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::find(Mapper::RequestsKe
y const&)
  0.06     88.13     0.05  9461440     0.01     0.01  std::_Hashtable<Mapper::RequestsKey, std::pair<Mapper::RequestsKey const, int>, std::allocator<std
::pair<Mapper::RequestsKey const, int> >, std::__detail::_Select1st, Mapper::RequestsKeyEqual, Mapper::RequestsHash, std::__detail::_Mod_range_hashing, 
std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<true, false, true> >::_M_find_before_node(uns
igned long, Mapper::RequestsKey const&, unsigned long) const
  0.06     88.18     0.05  1502077     0.03     0.03  Options::optstruct(char const*, bool)
  0.05     88.22     0.05  3993040     0.01    22.20  vmips::step()
  0.05     88.26     0.04  2757869     0.01     0.03  CPU::fetch(bool&, bool)
  0.03     88.29     0.03  3993040     0.01     0.01  CPU::pre_mem_access(bool&)
  0.03     88.32     0.03  2729387     0.01     0.01  CPU::decode()

アービタの話題は #7

3d22720: すべてのカウントをデクリメントするのではなく、最初のリクエスト時でのサイクル数を保持し、現在のサイクル数との差からreadyを算出するように仕様変更
a7651cf: store_wordでreadyの時リクエスト時のサイクル数を削除するように修正
1b55c04: 上に加え、readyでないときにbus_errorを起こすコミット
b14edc7: stall_emuをmerge

以下は b14edc7 を手元のMacBook Pro(Early 2015)で実行した時のシミュレーション時間

./cube_sim test_vec/jpeg.bin  0.72s user 0.01s system 98% cpu 0.740 total

速くなった。