LoginSignup
13
15

More than 5 years have passed since last update.

Linuxでユーザー空間で動作するプログラムとハードウェアがメモリを共有するためのデバイスドライバ(NumPy対応)

Last updated at Posted at 2017-01-29

はじめに

前回投稿した「Linuxでユーザー空間で動作するプログラムとハードウェアがメモリを共有するためのデバイスドライバで紹介したudmabufを NumPy(Pythonの数値計算ライブラリ)で使えるようにしました。具体的には、udmabuf でカーネル内に確保したバッファ領域を NumPy の memmap でマッピングして、ndarray と同じような操作が出来るようにします。この記事では、その方法について説明します。

udmabuf の更新

残念ながら udmabuf のバージョンが version 0.5.0(2016/4/24) 以前では、次のようなエラーが出ます。

shell# python
Python 2.7.9 (default, Aug 13 2016, 17:56:53)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> m = np.memmap('/dev/udmabuf0', dtype=np.uint8, mode='r+', shape=(100))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/dist-packages/numpy/core/memmap.py", line 217, in __new__
    fid.seek(0, 2)
IOError: [Errno 29] Illegal seek

これは udmabuf が lseek に対応していなかったのが原因です。どうやら NumPy の memmap は seek(0,2) を使ってファイルのサイズを得ているようですが、私が udmabuf に lseek() を実装するのを忘れていました。
udmabuf version 0.6.0(2017/1/29) で lseek に対応しましたので、NumPy で udmabuf を使う場合は、umdabuf version 0.6.0 以降を使ってください。

使用例

udmabuf のインストール

まずは udmabuf をインストールします。具体的な方法は、「Linuxでユーザー空間で動作するプログラムとハードウェアがメモリを共有するためのデバイスドライバ を参照してください。

次の例では udmabuf.ko を直接 insmod でインストールしています。その際、udmabuf0 として 8MByte のバッファを確保しています。

shell# insmod udmabuf.ko udmabuf0=8388608
[34654.590294] alloc_contig_range: [1f100, 1f900) PFNs busy
[34654.596154] alloc_contig_range: [1f200, 1fa00) PFNs busy
[34654.622746] udmabuf udmabuf0: driver installed
[34654.627153] udmabuf udmabuf0: major number   = 237
[34654.631889] udmabuf udmabuf0: minor number   = 0
[34654.636685] udmabuf udmabuf0: phys address   = 0x1f300000
[34654.642002] udmabuf udmabuf0: buffer size    = 8388608

udmabuf_test.py

次に python+NumPy による簡単なテストをするスクリプトを示します。

udmabuf_test.py
import numpy as np
import time

class Udmabuf:
    """A simple udmabuf class"""

    def __init__(self, name):
        self.name        = name
        self.device_name = '/dev/%s'               % self.name
        self.class_path  = '/sys/class/udmabuf/%s' % self.name
        for line in open(self.class_path + '/size'):
            self.buf_size = int(line)
            break

    def memmap(self, dtype, shape):
        self.item_size = np.dtype(dtype).itemsize
        self.mem_map   = np.memmap(self.device_name, dtype=dtype, mode='r+', shape=shape)

def test_1(a):
    for i in range (0,9):
        a *= 0
        a += 0x31

if __name__ == '__main__':
    udmabuf      = Udmabuf('udmabuf0')
    test_dtype   = np.uint8
    test_size    = int(udmabuf.buf_size/(np.dtype(test_dtype).itemsize))
    udmabuf.memmap(dtype=test_dtype, shape=(test_size))
    comparison   = np.zeros(test_size, dtype=test_dtype)
    print ("test_size  : %d" % test_size)
    start        = time.time()
    test_1(udmabuf.mem_map)
    elapsed_time = time.time() - start
    print ("udmabuf0   : elapsed_time:{0}".format(elapsed_time) + "[sec]")
    start        = time.time()
    test_1(comparison)
    elapsed_time = time.time() - start
    print ("comparison : elapsed_time:{0}".format(elapsed_time) + "[sec]")
    if np.array_equal(udmabuf.mem_map, comparison):
        print ("udmabuf0 == comparison : OK")
    else:
        print ("udmabuf0 != comparison : NG")

上記スクリプトでは、udmabuf でカーネル内に確保したバッファ領域を numpy.memmap で python から使えるようにしています。
numpy.memmap で生成されたオブジェクトは numpy.ndarray と同様の操作ができるようになります。
上記スクリプトでは a *=0 と a += 0x31 を10回繰り返して、実行時間を測定しています。

実行結果

前節のスクリプトを実行すると次のような結果になりました。

shell# python udmabuf_test.py
test_size  : 8388608
udmabuf0   : elapsed_time:1.53304982185[sec]
comparison : elapsed_time:1.536673069[sec]
udmabuf0 == comparison : OK

udmabuf0(カーネル内に確保したバッファ領域)に対する操作と、ndarray で同じ操作を行った場合(comparison)の実行時間はほぼ同じでした。すなわち udmabuf0 も CPU キャッシュが有効に働いていると思われます。

このスクリプトを実行した後で、udmabuf0 の内容を確認しました。

shell# dd if=/dev/udmabuf0 of=udmabuf0.bin bs=8388608
1+0 records in
1+0 records out
8388608 bytes (8.4 MB) copied, 0.151531 s, 55.4 MB/s
shell# 
shell# od -t x1 udmabuf0.bin
0000000 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31 31
*
40000000

スクリプト実行後も、実行した結果がバッファに残っていることが確認できました。
念のため、NumPy でも読めることを確認しましょう。

shell# python
Python 2.7.9 (default, Aug 13 2016, 17:56:53)
[GCC 4.9.2] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import numpy as np
>>> a = np.memmap('/dev/udmabuf0', dtype=np.uint8, mode='r+', shape=(8388608))
>>> a
memmap([49, 49, 49, ..., 49, 49, 49], dtype=uint8)
>>> a.itemsize
1
>>> a.size
8388608
>>>

まとめ

udmabuf を使ってカーネル内に確保したバッファ領域を python の NumPy で操作できるようになりました。これにより C で書かれたコードやライブラリを使うこと無く、python だけで 直接 FPGA の PL(Programmable Logic)部を扱えるようになります。

FPGA の PL(Programmable Logic)部 が NumPy 形式に対応することが出来れば、今よりももう少し簡単に CPU(Python+NumPy) と FPGA アクセラレータとが連携できるようになるでしょう。

13
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
15