Pilote de périphérique (compatible NumPy) pour les programmes et le matériel qui s'exécutent dans l'espace utilisateur sous Linux pour partager la mémoire

introduction

[Udmabuf](https: :) présenté dans le post précédent ["Pilote de périphérique pour les programmes et le matériel s'exécutant dans l'espace utilisateur sous Linux pour partager la mémoire" (http://qiita.com/ikwzm/items/cc1bb33ff43a491440ea)] //github.com/ikwzm/udmabuf) est désormais disponible dans NumPy (la bibliothèque de calcul numérique de Python). Plus précisément, la zone tampon allouée dans le noyau par udmabuf est mappée par le memmap et ndarray de NumPy. Dans cet article, je vais vous expliquer comment le faire.

mise à jour udmabuf

Malheureusement, si la version d'udmabuf est la version 0.5.0 (2016/4/24) ou antérieure, vous obtiendrez l'erreur suivante.

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

En effet, udmabuf ne supportait pas lseek. Apparemment, memmap de NumPy utilise seek (0,2) pour obtenir la taille du fichier, mais j'ai oublié d'implémenter lseek () dans udmabuf. udmabuf version 0.6.0 (2017/1/29) prend en charge lseek, donc si vous souhaitez utiliser udmabuf avec NumPy, veuillez utiliser umdabuf version 0.6.0 ou ultérieure.

Exemple d'utilisation

Installez udmabuf

Tout d'abord, installez udmabuf. Pour la méthode spécifique, reportez-vous à ["Pilote de périphérique pour les programmes et le matériel s'exécutant dans l'espace utilisateur sous Linux pour partager la mémoire" (http://qiita.com/ikwzm/items/cc1bb33ff43a491440ea).

L'exemple suivant installe udmabuf.ko directement avec insmod. À ce moment-là, un tampon de 8 Mo est sécurisé en tant que udmabuf0.

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

Voici un script qui effectue un test simple avec 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")
    

Dans le script ci-dessus, la zone tampon allouée dans le noyau par udmabuf est rendue disponible à partir de python par numpy.memmap. Les objets créés par numpy.memmap peuvent être utilisés de la même manière que numpy.ndarray. Dans le script ci-dessus, a * = 0 et a + = 0x31 sont répétés 10 fois pour mesurer le temps d'exécution.

Résultat d'exécution

Lorsque j'ai exécuté le script dans la section précédente, j'ai obtenu le résultat suivant.

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

Le temps d'exécution de l'opération sur udmabuf0 (zone tampon allouée dans le noyau) et le temps d'exécution de la même opération sur ndarray (comparaison) étaient presque les mêmes. En d'autres termes, udmabuf0 semble également que le cache du processeur fonctionne efficacement.

Après avoir exécuté ce script, j'ai vérifié le contenu de 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

Après avoir exécuté le script, j'ai pu confirmer que le résultat de l'exécution restait dans le tampon. Au cas où, assurez-vous de pouvoir le lire avec 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
>>>

Résumé

Vous pouvez maintenant utiliser NumPy de python pour manipuler la zone tampon allouée dans le noyau en utilisant udmabuf. Cela permet de gérer la partie PL (Programmable Logic) de FPGA directement avec python seul, sans utiliser de code ou de bibliothèques écrits en C.

Si la partie PL (Programmable Logic) du FPGA peut prendre en charge le format NumPy, il sera un peu plus facile que maintenant de relier le CPU (Python + NumPy) à l'accélérateur FPGA.

Recommended Posts

Pilote de périphérique (compatible NumPy) pour les programmes et le matériel qui s'exécutent dans l'espace utilisateur sous Linux pour partager la mémoire
Pilote de périphérique pour les programmes et le matériel s'exécutant dans l'espace utilisateur sous Linux pour partager la mémoire (V4L2_MEMORY_USERPTR)
Comment exécuter python dans l'espace virtuel (pour MacOS)
Exécutez le pilote Lima sur Debian GNU / Linux pour Ultra96 / Ultra96-V2
Comment installer OpenCV sur Cloud9 et l'exécuter en Python
Pilote de périphérique pour accéder à la mémoire FPGA à partir de Linux