zFIFO - an AXI DMA driver for Zynq and ZynqMP

性能チューニング

クロック周波数の調整

ZynqMP では、PS から供給される PL のクロックが fclk ドライバによって制御されており、sysfs から変更することができます。たとえば、

$ echo 200000000 | sudo tee /sys/devices/platform/fclk0/set_rate
$ cat /sys/devices/platform/fclk0/set_rate
187499999

のような感じです。この例では 200MHz にセットして、実際には 187MHz ですが、これは PS の PLL の事情などもありますので、必ずしも希望したクロック周波数ぴったりになるとは限らないためです。周波数の調整は、PL の回路がアイドル状態ならいつ行っても大丈夫なようですが、気になる場合はリセットできるような仕掛けを用意しておいたり、周波数変更後に JTAG から書き込み直したりするといいかもしれません。

AXI Stream バンド幅の変更

PS の Slave AXI インタフェイスは ZynqMP では 128bit, Zynq-7000 では 64bit です。例題として配布している DMA loopback のデザインでは、ループバック用の FIFO のデータ幅が 32bit なので、ブロックデザインを開いて AXI DMA の Stream Data Width を 64bit や 128bit に変更すれば、理論上はより速い転送レートを得ることができます。このあたりは、下記の性能評価の結果なども参考にしつつ、実装するアプリケーションで使いやすいストリーム幅にするのがよいでしょう。

性能測定

bw.png

この図は以下のようなコードを用いて、Ultra96 (無印) で測定を行ったものです。往復のバンド幅ですから、片方向の場合はグラフの縦軸の値を2で割ることになります。ピークのバンド幅はおおよそ、

となります。クロック周波数を上げてもあまり性能が伸びないのは、Linux カーネル内部での仮想メモリ操作に関わるオーバーヘッドが大きいからのようです。設計のご参考にどうぞ。

  1. // Bandwidth tester
  2.  
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <unistd.h>
  6. #include <sys/types.h>
  7. #include <sys/stat.h>
  8. #include <fcntl.h>
  9. #include <sys/ioctl.h>
  10. #include <omp.h>
  11.  
  12. #include "zfifo.h"
  13. #include "mysecond.c"
  14.  
  15. int main(){
  16.   int fd = open("/dev/zfifo0", O_RDWR | O_SYNC);
  17.   if (fd<0) {
  18.     printf("Can't open /dev/zfifo0!\n");
  19.     return -1;
  20.   }
  21.  
  22.   unsigned maxsize = 8*1024*1024;
  23.   unsigned *send = (unsigned*)malloc(maxsize * sizeof(unsigned));
  24.   unsigned *recv = (unsigned*)malloc(maxsize * sizeof(unsigned));
  25.  
  26.   for (unsigned i=0; i<maxsize; i++){
  27.     send[i] = i;
  28.     recv[i] = 0;
  29.   }
  30.  
  31.   for (int size=4; size<=maxsize; size*=2){
  32.     int ntimes = maxsize / size;
  33.     if (ntimes<  100) ntimes =  100;
  34.     if (ntimes> 1000) ntimes = 1000;
  35.  
  36.     printf("size %d ntimes %d ", (int)size*sizeof(unsigned), (int)ntimes);
  37.  
  38.     double start = mysecond();
  39.     for (int n=0; n<ntimes; n++){
  40.  
  41.       // Send & recv in parallel because FIFO deadlocks
  42. #pragma omp parallel for
  43.       for(int i=0; i<2; i++){
  44.         if (i==0){
  45.           // printf("Send on thread %d\n", omp_get_thread_num());
  46.           zf_send(fd, (char*)send, sizeof(unsigned)*size);
  47.         }
  48.         if (i==1){
  49.           // printf("Recv on thread %d\n", omp_get_thread_num());
  50.           zf_recv(fd, (char*)recv, sizeof(unsigned)*size);
  51.         }
  52.       }
  53.     }
  54.     double stop = mysecond();
  55.     double elapsed = stop-start;
  56.  
  57.     size_t total_size = ntimes * size * 2 * sizeof(unsigned);
  58.     double throughput = (double)total_size/(1000*1000*elapsed);
  59.  
  60.     printf("%lf sec %lf MB/s\n", elapsed, throughput);
  61.   }
  62.  
  63.   close(fd);
  64.   return 0;
  65. }

Front page   New Page list Search Recent changes   Help   RSS of recent changes