ARMマむクロプロセッサのキャッシュに぀いお

画像こんにちは



前回の蚘事では、プロセッサキャッシュを䜿甚しお、Emboxのマむクロコンピュヌタのグラフィックスを高速化したした。この堎合、「ラむトスルヌ」モヌドを䜿甚したした。次に、「ラむトスルヌ」モヌドに関連するいく぀かの長所ず短所に぀いお説明したしたが、これは倧たかな抂芁にすぎたせんでした。この蚘事では、玄束どおり、ARMマむクロプロセッサのキャッシュの皮類を詳しく調べお比范したいず思いたす。もちろん、これはすべおプログラマヌの芳点から怜蚎されたす。この蚘事では、メモリヌコントロヌラヌの詳现に぀いおは説明したせん。



前回の蚘事で停止したずころ、぀たり「ラむトバック」モヌドず「ラむトスルヌ」モヌドの違いから始めたす。これらの2぀のモヌドが最も頻繁に䜿甚されるためです。芁するに



  • "返事を曞く"。曞き蟌みデヌタはキャッシュにのみ送られたす。メモリぞの実際の曞き蟌みは、キャッシュがいっぱいになり、新しいデヌタ甚のスペヌスが必芁になるたで延期されたす。
  • 「ラむトスルヌ」。キャッシュずメモリぞの曞き蟌みは「同時に」行われたす。


ラむトスルヌ



ラむトスルヌの利点は䜿いやすさであるず考えられおおり、゚ラヌを枛らす可胜性がありたす。実際、このモヌドでは、メモリは垞に正しい状態にあり、远加の曎新手順は必芁ありたせん。



もちろん、これはパフォヌマンスに倧きな圱響を䞎えるはずですが、このドキュメントのSTM自䜓は、そうではないず述べおいたす。

Write-through: triggers a write to the memory as soon as the contents on the cache line are written to. This is safer for the data coherency, but it requires more bus accesses. In practice, the write to the memory is done in the background and has a little effect unless the same cache set is being accessed repeatedly and very quickly. It is always a tradeoff.
぀たり、最初は曞き蟌みがメモリぞの曞き蟌みであるため、曞き蟌み操䜜のパフォヌマンスはキャッシュがない堎合ずほが同じであり、読み取りが繰り返されるために䞻なゲむンが発生するず想定しおいたした。ただし、STMはこれに異議を唱え、メモリ内のデヌタは「バックグラりンド」になるため、曞き蟌みパフォヌマンスは「ラむトバック」モヌドずほが同じであるず述べおいたす。これは、特に、メモリコントロヌラFMCの内郚バッファに䟝存する堎合がありたす。



「ラむトスルヌ」モヌドのデメリット



  • 同じメモリぞのシヌケンシャルで高速なアクセスは、パフォヌマンスを䜎䞋させる可胜性がありたす。「ラむトバック」モヌドでは、逆に、同じメモリぞの連続した頻繁なアクセスはプラスになりたす。
  • 「ラむトバック」の堎合ず同様に、DMA操䜜の終了埌にキャッシュの無効化を行う必芁がありたす。
  • Cortex-M7の䞀郚のバヌゞョンで、「䞀連のラむトスルヌストアおよびロヌドでのデヌタ砎損」のバグ。それはされた指摘に私たちLVGLの開発者の䞀人で。


返事を曞く



䞊蚘のように、このモヌドでは「ラむトスルヌ」ずは察照的に、デヌタは通垞、曞き蟌みによっおメモリに入るのではなく、キャッシュに入るだけです。ラむトスルヌず同様に、この戊略には2぀のサブオプションがありたす。1曞き蟌み割り圓お、2曞き蟌み割り圓おなし。これらのオプションに぀いおさらに説明したす。



曞き蟌み割り圓お



原則ずしお、「読み取り割り圓お」は垞にキャッシュで䜿甚されたす。぀たり、読み取りのキャッシュミスが発生するず、デヌタがメモリからフェッチされ、キャッシュに配眮されたす。同様に、曞き蟌みミスが発生した堎合、デヌタをキャッシュにロヌドする「曞き蟌み割り圓お」か、ロヌドしない「曞き蟌み割り圓おなし」こずができたす。



通垞、実際には、「ラむトバック曞き蟌み割り圓お」たたは「ラむトスルヌ曞き蟌み割り圓おなし」の組み合わせが䜿甚されたす。さらにテストでは、「曞き蟌み割り圓お」を䜿甚する状況ず「曞き蟌み割り圓おなし」を䜿甚する状況に぀いお、もう少し詳しく確認したす。



MPU



実際の郚分に進む前に、メモリ領域のパラメヌタを蚭定する方法を理解する必芁がありたす。ARMv7-Mアヌキテクチャのメモリの特定の領域に察しおキャッシュモヌドを遞択たたは無効化するには、MPUメモリ保護ナニットを䜿甚したす。



MPUコントロヌラは、メモリ領域の蚭定をサポヌトしおいたす。具䜓的には、ARMV7-Mアヌキテクチャでは、最倧16のリヌゞョンが存圚する可胜性がありたす。これらのリヌゞョンでは、開始アドレス、サむズ、アクセス暩読み取り/曞き蟌み/実行など、属性-TEX、キャッシュ可胜、バッファ可胜、共有可胜、およびその他のパラメヌタを個別に蚭定できたす。特に、このようなメカニズムを䜿甚するず、特定のリヌゞョンに察しお任意のタむプのキャッシングを実珟できたす。たずえば、すべおのDMA操䜜にメモリの領域を割り圓お、そのメモリをキャッシュ䞍可ずしおマヌクするだけで、cache_clean / cache_invalidateを呌び出す必芁をなくすこずができたす。



MPUを䜿甚する際の泚意点

リヌゞョンのベヌスアドレス、サむズ、および属性はすべお構成可胜であり、䞀般的なルヌルでは、すべおのリヌゞョンが自然に敎列されたす。これは次のように

衚すこずができたす。RegionBaseAddress[N-10] = 0、ここでNはlog2SizeofRegion_in_bytes
぀たり、メモリ領域の開始アドレスを独自のサむズに揃える必芁がありたす。たずえば、16 Kbの領域がある堎合は、16Kbで䜍眮合わせする必芁がありたす。メモリ領域が64Kbの堎合は、64Kbに揃えたす。等々。これが行われない堎合、MPUは、開始アドレスに察応するサむズに領域を自動的に「トリミング」できたす実際にテスト枈み。



ちなみに、STM32Cubeにはいく぀かのバグがありたす。䟋えば



  MPU_InitStruct.BaseAddress = 0x20010000;
  MPU_InitStruct.Size = MPU_REGION_SIZE_256KB;


開始アドレスが64KBに揃えられおいるこずがわかりたす。たた、領域のサむズを256KBにする必芁がありたす。この堎合、最初の64 Kb、2番目の128 Kb、3番目の64Kbの3぀の領域を䜜成する必芁がありたす。



暙準のプロパティずは異なる領域を指定するだけで枈みたす。実際には、プロセッサキャッシュが有効になっおいる堎合のすべおのメモリの属性は、ARMアヌキテクチャで蚘述されおいたす。プロパティの暙準セットがありたすたずえば、これがSTM32F7 SRAMにデフォルトで「ラむトバック曞き蟌み割り圓お」モヌドがある理由です。したがっお、䞀郚のメモリに非暙準モヌドが必芁な堎合は、MPUを介しおプロパティを蚭定する必芁がありたす。この堎合、リヌゞョン内で、独自のプロパティを持぀サブリヌゞョンを蚭定できたす。このリヌゞョン内で、必芁なプロパティを持぀優先床の高い別のサブリヌゞョンを匷調衚瀺したす。



TCM



ドキュメントセクション2.3組み蟌みSRAM からわかるように、STM32F7のSRAMの最初の64KBはキャッシュできたせん。ARMv7-Mアヌキテクチャ自䜓では、SRAMは0x20000000にありたす。TCMはSRAMも指したすが、残りのメモリSRAM1およびSRAM2ずは異なるバス䞊にあり、プロセッサの「近く」にありたす。このため、このメモリは非垞に高速であり、実際、キャッシュず同じ速床です。このため、キャッシュは䞍芁であり、この領域をキャッシュ可胜にするこずはできたせん。実際、TCMもそのようなキャッシュです。



呜什キャッシュ



䞊蚘で説明したものはすべお、デヌタキャッシュD-Cacheを参照しおいるこずに泚意しおください。ただし、ARMv7-Mは、デヌタキャッシュに加えお、呜什キャッシュ呜什キャッシュI-Cacheも提䟛したす。I-Cacheを䜿甚するず、実行可胜なおよび埌続の呜什の䞀郚をキャッシュに転送できたす。これにより、プログラムを倧幅に高速化できたす。特に、QSPIなど、コヌドがFLASHよりも遅いメモリにある堎合。



以䞋のキャッシュを䜿甚したテストでの予枬䞍可胜性を枛らすために、意図的にI-Cacheを無効にし、デヌタのみを考慮したす。



同時に、I-Cacheをオンにするこずは非垞に簡単であり、D-Cacheずは異なり、MPUからの远加のアクションを必芁ずしないこずに泚意したいず思いたす。



合成テスト



理論的な郚分に぀いお説明した埌、特定のモデルの違いず適甚範囲をよりよく理解するために、テストに移りたしょう。䞊で述べたように、I-Cacheを無効にし、D-Cacheでのみ機胜したす。たた、テストのルヌプが最適化されないように、意図的に-O0を䜿甚しおコンパむルしたす。倖郚SDRAMメモリを介しおテストしたす。 MPUの助けを借りお、64 KBの領域にマヌクを付け、この領域に必芁な属性を公開したす。



キャッシュを䜿甚したテストは非垞に気たぐれであり、システム内のすべおのものずすべおの人の圱響を受けるため、コヌドを線圢で連続的にしたしょう。これを行うには、割り蟌みを無効にしたす。たた、タむマヌでは時間を枬定したせんが、プロセッササむクルの32ビットカりンタを備えたDWTデヌタりォッチポむントおよびトレヌスナニットを枬定したす。その䞊でむンタヌネット䞊で人々はドラむバヌにマむクロ秒の遅延を䜜りたす。カりンタは216MHzのシステム呚波数ですぐにオヌバヌフロヌしたすが、最倧20秒たで枬定できたす。これを念頭に眮いお、この時間間隔でテストを行い、開始する前にクロックカりンタヌを事前にれロにしたす。



完党なテストコヌドはここで確認できたす。すべおのテストは、32F769IDISCOVERYボヌドで実行されたした。



キャッシュ䞍可胜なメモリVS。返事を曞く



それでは、いく぀かの非垞に簡単なテストから始めたしょう。



䞀貫しおメモリに曞き蟌みたす。



    dst = (uint8_t *) DATA_ADDR;

    for (i = 0; i < ITERS * 8; i++) {
        for (j = 0; j < DATA_LEN; j++) {
            *dst = VALUE;
            dst++;
        }
        dst -= DATA_LEN;
    }


たた、メモリに順番に曞き蟌みたすが、䞀床に1バむトではありたせんが、ルヌプを少し拡匵したす。



    for (i = 0; i < ITERS * BLOCKS * 8; i++) {
        for (j = 0; j < BLOCK_LEN; j++) {
            *dst = VALUE;
            *dst = VALUE;
            *dst = VALUE;
            *dst = VALUE;
            dst++;
        }
        dst -= BLOCK_LEN;
    }


たた、メモリに順次曞き蟌みたすが、ここで読み取りも远加したす。



    for (i = 0; i < ITERS * BLOCKS * 8; i++) {
        dst = (uint8_t *) DATA_ADDR;

        for (j = 0; j < BLOCK_LEN; j++) {
            val = VALUE;
            *dst = val;
            val = *dst;
            dst++;
        }
    }


これら3぀のテストをすべお実行するず、どのモヌドを遞択しおも、たったく同じ結果が埗られたす。



mode: nc, iters=100, data len=65536, addr=0x60100000
Test1 (Sequential write):
  0s 728ms
Test2 (Sequential write with 4 writes per one iteration):
  7s 43ms
Test3 (Sequential read/write):
  1s 216ms


そしお、これは合理的です。特に、SDRAMが接続されおいるFMCの内郚バッファヌを考慮するず、SDRAMはそれほど遅くはありたせん。それにもかかわらず、私は数のわずかな倉動を予想したした、しかしそれはこれらのテストになかったこずがわかりたした。さお、さらに考えおみたしょう。



読み取りず曞き蟌みを組み合わせお、SDRAMの寿呜を「台無し」にしおみたしょう。これを行うには、ルヌプを拡匵しお、実際には配列芁玠の増分などの䞀般的なものを远加したしょう。



    for (i = 0; i < ITERS * BLOCKS; i++) {
        for (j = 0; j < BLOCK_LEN; j++) {
            // 16 lines
            arr[i]++;
            arr[i]++;
	***
            arr[i]++;
        }
    }


結果



  :   4s 743ms
Write-back:                     :   4s 187ms


すでに優れおいたす-キャッシュを䜿甚するず、0.5秒速くなるこずがわかりたした。テストをさらに耇雑にしおみたしょう。「スパヌス」むンデックスによるアクセスを远加したす。たずえば、1぀のむンデックスがある堎合



    for (i = 0; i < ITERS * BLOCKS; i++) {
        for (j = 0; j < BLOCK_LEN; j++) {
            arr[i + 0 ]++;
            ***
            arr[i + 3 ]++;
            arr[i + 4 ]++;
            arr[i + 100]++;
            arr[i + 6 ]++;
            arr[i + 7 ]++;
            ***
            arr[i + 15]++;
        }
    }


結果



  :   11s 371ms
Write-back:                     :   4s 551ms


これで、キャッシュずの違いが目立぀ようになりたした。さらに、そのような2番目のむンデックスを玹介したす。



    for (i = 0; i < ITERS * BLOCKS; i++) {
        for (j = 0; j < BLOCK_LEN; j++) {
            arr[i + 0 ]++;
            ***
            arr[i + 4 ]++;
            arr[i + 100]++;
            arr[i + 6 ]++;
            ***
            arr[i + 9 ]++;
            arr[i + 200]++;
            arr[i + 11]++;
            arr[i + 12]++;
            ***
            arr[i + 15]++;
        }
    }


結果



  :   12s 62ms
Write-back:                     :   4s 551ms


キャッシュされおいないメモリの時間がほが1秒増加し、キャッシュの堎合は同じたたであるこずがわかりたす。



曞き蟌み割り圓おVS。曞き蟌み割り圓おなし



それでは、「曞き蟌み割り圓お」モヌドを扱いたしょう。ここで違いを確認するのはさらに困難です。キャッシュされおいないメモリず「ラむトバック」の間の状況で、4番目のテストからすでに明確に衚瀺されおいる堎合、「曞き蟌み割り圓お」ず「曞き蟌み割り圓おなし」の違いはテストによっおただ明らかにされおいたせん。考えおみたしょう-「曞き蟌み割り圓お」が速くなるのはい぀ですかたずえば、シヌケンシャルメモリ䜍眮ぞの曞き蟌みが倚く、それらのメモリ䜍眮からの読み取りが少ない堎合です。この堎合、「曞き蟌み割り圓おなし」モヌドでは、垞にミスが発生し、読み取りによっお間違った芁玠がキャッシュにロヌドされたす。この状況をシミュレヌトしおみたしょう。



    for (i = 0; i < ITERS * BLOCKS; i++) {
        for (j = 0; j < BLOCK_LEN; j++) {
            arr[j + 0 ]  = VALUE;
            ***
            arr[j + 7 ]  = VALUE;
            arr[j + 8 ]  = arr[i % 1024 + (j % 256) * 128];
            arr[j + 9 ]  = VALUE;
            ***
            arr[j + 15 ]  = VALUE;
        }
    }


ここでは、16レコヌドのうち15レコヌドがVALUE定数に蚭定され、読み取りは異なる曞き蟌みずは関係のない芁玠から実行されたすarr [i1024 +j256* 128]。曞き蟌み割り圓おなしの戊略では、これらの芁玠のみがキャッシュにロヌドされるこずがわかりたす。このようなむンデックスが䜿甚される理由i1024 +j256* 128は、FMC / SDRAMの「速床䜎䞋」です。倧幅に異なる非順次アドレスでのメモリアクセスは、䜜業速床に倧きな圱響を䞎える可胜性があるためです。



結果



Write-back                                           :   4s 720ms
Write-back no write allocate:               :   4s 888ms


最埌に、それほど目立たないものの、すでに目に芋える違いがありたした。぀たり、私たちの仮説が確認されたした。



そしお最埌に、私の意芋では、最も難しいケヌスです。「曞き蟌み割り圓おなし」が「曞き蟌み割り圓お」よりも優れおいる堎合を理解したいず思いたす。近い将来に䜿甚しないアドレスを「頻繁に」参照する堎合は、最初の方が適しおいたす。このようなデヌタをキャッシュする必芁はありたせん。



次のテストでは、「曞き蟌み割り圓お」の堎合、デヌタは読み取りず曞き蟌みで入力されたす。64KBの配列「arr2」を䜜成したので、新しいデヌタを亀換するためにキャッシュがフラッシュされたす。「曞き蟌み割り圓おなし」の堎合、4096バむトの「arr」配列を䜜成したしたが、それだけがキャッシュに入りたす。぀たり、キャッシュデヌタはメモリにフラッシュされたせん。このため、少なくずも小さな勝利を目指したす。



    arr = (uint8_t *) DATA_ADDR;
    arr2 = arr;

    for (i = 0; i < ITERS * BLOCKS; i++) {
        for (j = 0; j < BLOCK_LEN; j++) {
            arr2[i * BLOCK_LEN            ] = arr[j + 0 ];
            arr2[i * BLOCK_LEN + j*32 + 1 ] = arr[j + 1 ];
            arr2[i * BLOCK_LEN + j*64 + 2 ] = arr[j + 2 ];
            arr2[i * BLOCK_LEN + j*128 + 3] = arr[j + 3 ];
            arr2[i * BLOCK_LEN + j*32 + 4 ] = arr[j + 4 ];
            ***
            arr2[i * BLOCK_LEN + j*32 + 15] = arr[j + 15 ];
        }
    }


結果



Write-back                                           :   7s 601ms
Write-back no write allocate:               :   7s 599ms


「ラむトバック」「ラむトアロケヌション」モヌドの方がわずかに高速であるこずがわかりたす。しかし、重芁なこずはそれがより速いずいうこずです。



これ以䞊のデモンストレヌションは埗られたせんでしたが、違いがより明確になる実際的な状況があるず確信しおいたす。読者は自分のオプションを提案するこずができたす



実䟋



合成の䟋から実際の䟋に移りたしょう。



ping



最も簡単なものの1぀はpingです。開始は簡単で、時間はホストで盎接衚瀺できたす。Emboxは-O2最適化で構築されたした。すぐに結果をお䌝えしたしょう。



    :  ~0.246 c
Write-back                        :  ~0.140 c


Opencv



キャッシュサブシステムを詊しおみたかった実際の問題のもう1぀の䟋は、STM32F7のOpenCVです。その蚘事では、起動はかなり可胜であるこずが瀺されおいたしたが、パフォヌマンスはかなり䜎かったです。デモンストレヌションでは、Cannyフィルタヌに基づいお境界線を抜出する暙準的な䟋を䜿甚したす。キャッシュDキャッシュずIキャッシュの䞡方がある堎合ずない堎合の実行時間を枬定しおみたしょう。



   gettimeofday(&tv_start, NULL);

    cedge.create(image.size(), image.type());
    cvtColor(image, gray, COLOR_BGR2GRAY);

    blur(gray, edge, Size(3,3));
    Canny(edge, edge, edgeThresh, edgeThresh*3, 3);
    cedge = Scalar::all(0);

    image.copyTo(cedge, edge);

    gettimeofday(&tv_cur, NULL);
    timersub(&tv_cur, &tv_start, &tv_cur);


キャッシュなし



> edges fruits.png 20 
Processing time 0s 926ms
Framebuffer: 800x480 32bpp
Image: 512x269; Threshold=20


キャッシュあり



> edges fruits.png 20 
Processing time 0s 134ms
Framebuffer: 800x480 32bpp
Image: 512x269; Threshold=20


぀たり、926msず134msの加速はほが7倍です。



実際、STM32のOpenCVに぀いお、特にパフォヌマンスに぀いおよく尋ねられたす。FPSは確かに高くはありたせんが、1秒あたり5フレヌムを取埗するのは非垞に珟実的です。



キャッシュ可胜たたはキャッシュされたメモリではありたせんが、キャッシュを無効にしたすか



実際のデバむスでは、DMAが広く䜿甚されおいたすが、「ラむトスルヌ」モヌドでもメモリを同期する必芁があるため、圓然、DMAには問題が䌎いたす。キャッシュされないメモリを単に割り圓おお、DMAを䜿甚するずきにそれを䜿甚したいずいうのは圓然のこずです。少し気が散る。 Linuxでは、これはdma_coherent_allocを介した関数によっお実行されたす。はい、これは非垞に効果的な方法です。たずえば、OSでネットワヌクパケットを操䜜する堎合、ナヌザヌデヌタはドラむバヌに到達する前に凊理の倧きな段階を経お、ドラむバヌでは、すべおのヘッダヌを含む準備されたデヌタが、キャッシュされおいないメモリを䜿甚するバッファヌにコピヌされたす。



DMAを備えたドラむバヌでクリヌン/無効化が望たしい堎合はありたすかはいありたす。たずえば、私たちを促したビデオメモリcacheがどのように機胜するかを詳しく芋おみたしょう。ダブルバッファリングモヌドでは、システムには2぀のバッファがあり、そこに順番に匕き蟌たれ、ビデオコントロヌラに枡されたす。このようなメモリをキャッシュ䞍可にするず、パフォヌマンスが䜎䞋したす。したがっお、バッファをビデオコントロヌラに送信する前に、クリヌンアップを実行するこずをお勧めしたす。



結論



ARMv7mのさたざたなタむプのキャッシュに぀いお少し理解したした。ラむトバック、ラむトスルヌ、および「曞き蟌み割り圓お」ず「曞き蟌み割り圓おなし」の蚭定です。あるモヌドが他のモヌドよりも優れおいるこずを確認するための合成テストを䜜成し、pingずOpenCVを䜿甚した実際的な䟋も怜蚎したした。Emboxでは、このトピックに取り組んでいるだけなので、察応するサブシステムはただ怜蚎䞭です。ただし、キャッシュを䜿甚する利点は間違いなく顕著です。



すべおの䟋は、オヌプンリポゞトリからEmboxをビルドするこずで衚瀺および耇補できたす。



PS



あなたは、システムのプログラミングずOSDevの話題に興味がある堎合は、OSの日の䌚議が明日開催されたす今幎はオンラむンですので、お芋逃しなくEmboxは明日12.00に挔奏したす



All Articles