ZYNQのLinuxで、PLとの共有データをTCPやUDPを送信する速度を測ってみた
ZYNQのPLで管理しているDDR3メモリ内のデータを次から次へとネットワークに投げるような機能を開発しています。PLに溜めたデータをどれだけの速度でホストPCに転送できるかという実験で、「Cosmo-Z」という12bit 125MHz 8chの計測ボード用の新機能です。
言い換えれば、「ZYNQのLinuxにおいて、PLとの共有メモリ上のデータを、ネットワークにひたすら投げる」というプログラムを作っているわけなのですが、どのくらいの速度が出るでしょうか?実際に試してみました。
プログラムの中心となる部分は以下のようなルーチンです。
unsigned char *testbuf = (unsigned char *)malloc(1048576);
int cur = len;
int sent = 0;
struct timeval start,end;
gettimeofday(&start, NULL);
while(len) {
cur = len;
if(cur > 163840) cur = 163840;
sent += write(sock, testbuf , cur);
len -= cur;
}
gettimeofday(&end, NULL);
uint64_t elapse = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_usec - start.tv_usec);
printf("%f[MB/sec] %d[bytes] %lld[us]\n" , (double)sent / elapse, sent, elapse);
これを簡単に説明すると、testbufの内容をソケットに送信しているだけです。
gettimeofdayで、送信ルーチンの経過時間を計っています。
簡単ですね。
ZYNQ Linuxの最大送信サイズは163840バイトなので(SO_SNDBUFの値をgetsockoptで調べるとわかる)、大きなデータを送信する場合はこのサイズに区切って送ります。また、上のプログラムのwriteをsendtoに変えれば、同様の手口でUDPの速度もわかります。
その結果ですが、
このように、mallocで確保したローカルバッファから送るのであればTCPでは100Mバイト/秒超(!!)の速度が、UDPでは98Mバイト/秒の速度が出ていました。ギガビットのイーサの速度をほぼ使い切っている計算となります。(TCPの速度はギガビットを超えてしまっていますが、おそらくTCP/IPライブラリの中にバッファされているのでしょう。)
Cosmo-Zは計測したデータをFPGA管理下のメモリに置いてLinuxからは直接見えます。計測データをLinuxのプログラムから読み出すために、uio (user mode io)というドライバを使っています。
上のプログラムで、送信するデータをuio管理下のバッファにしてみます。
uint32_t *p = uio_mems + offset/4;
sent += sendto(sock, p , cur, 0, (struct sockaddr *)&addr, sizeof(addr));
すると、
なんと!28Mバイト/秒しか出ません。200Mbps程度の速度ということになります。
これを、
uint32_t *p = uio_mems + offset/4;
memcpy(testbuf, p, cur);
sent += sendto(sock, testbuf , cur, 0, (struct sockaddr *)&addr, sizeof(addr));
のようにuioのバッファからローカルのバッファにコピーしてから転送した場合、
40Mバイト/秒の速度が出ました。
つまり、
- UIOのバッファから直接TCP/UDPで送信した場合は28Mバイト/秒くらい
- UIOのバッファからローカルにmemcpyして送信した場合は40Mバイト/秒くらい
- ローカルのバッファから送信した場合は98Mバイト/秒くらい
ということになりました。
UIOが遅いのは目をつむるとして、ローカルにmemcpyして送ったほうが速くなるのが意外ですね。
抜本的な速度改善策としては、ZYNQのPLとPSで共有するメモリはスキャッタギャザーDMAを使ってローカルのバッファに直接マッピングするのがよいのだと思われます。ただ、それはとてつもなく難しそうです。
最近のコメント