[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.
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.
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.
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
>>>
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