interfacer Python et Torch7 (Lua) via une bibliothèque partagée

J’essaie de transmettre des données (tableaux) entre python et lua et je souhaite manipuler les données dans lua à l’aide du cadre Torch7. Je pensais que cela pouvait être mieux fait en C, car python et lua se connectaient avec C. De plus, certains avantages sont qu’aucune copie de données n’est nécessaire de cette façon (passage uniquement des pointeurs) et est rapide.

J’ai implémenté deux programmes, l’un dans lequel lua est intégré à c et l’autre dans lequel python transmet des données à c. Ils fonctionnent tous les deux lorsqu’ils sont compilés en fichiers binarys exécutables. Cependant, lorsque le programme c to lua est conçu pour être une bibliothèque partagée, les choses ne fonctionnent pas.

Les détails: J’utilise Ubuntu 14.04 et 12.04 64 bits. J’utilise Luajit 2.0.2 avec Lua 5.1 installé dans / usr / local / Dependency Les bibliothèques sont dans / usr / local / lib et les en-têtes sont dans / usr / local / include J’utilise python 2.7

Le code du programme c to lua est:

tensor.lua

require 'torch' function hi_tensor(t) print('Hi from lua') torch.setdefaulttensortype('torch.FloatTensor') print(t) return t*2 end 

cluaf.h

 void multiply (float* array, int m, int n, float *result, int m1, int n1); 

cluaf.c

 #include  #include  #include "lua.h" #include "lauxlib.h" #include "lualib.h" #include "luaT.h" #include "TH/TH.h" void multiply (float* array, int m, int n, float *result, int m1, int n1) { lua_State *L = luaL_newstate(); luaL_openlibs( L ); // loading the lua file if (luaL_loadfile(L, "tensor.lua") || lua_pcall(L, 0, 0, 0)) { printf("error: %s \n", lua_tossortingng(L, -1)); } // convert the c array to Torch7 specific structure representing a tensor THFloatStorage* storage = THFloatStorage_newWithData(array, m*n); THFloatTensor* tensor = THFloatTensor_newWithStorage2d(storage, 0, m, n, n, 1); luaT_newmetatable(L, "torch.FloatTensor", NULL, NULL, NULL, NULL); // load the lua function hi_tensor lua_getglobal(L, "hi_tensor"); if(!lua_isfunction(L,-1)) { lua_pop(L,1); } //this pushes data to the stack to be used as a parameter //to the hi_tensor function call luaT_pushudata(L, (void *)tensor, "torch.FloatTensor"); // call the lua function hi_tensor if (lua_pcall(L, 1, 1, 0) != 0) { printf("error running function `hi_tensor': %s \n", lua_tossortingng(L, -1)); } // get results returned from the lua function hi_tensor THFloatTensor* z = luaT_toudata(L, -1, "torch.FloatTensor"); lua_pop(L, 1); THFloatStorage *storage_res = z->storage; result = storage_res->data; return ; } 

Puis pour tester je fais:

 luajit -b tensor.lua tensor.o gcc -w -c -Wall -Wl,-E -fpic cluaf.c -lluajit -lluaT -lTH -lm -ldl -L /usr/local/lib gcc -shared cluaf.o tensor.o -L/usr/local/lib -lluajit -lluaT -lTH -lm -ldl -Wl,-E -o libcluaf.so gcc -L. -Wall -o test main.c -lcluaf ./test 

Le résultat:

 Hi from lua 1.0000 0.2000 0.2000 5.3000 [torch.FloatTensor of dimension 2x2] c result 2.000000 c result 0.400000 c result 0.400000 c result 10.60000 

Jusqu’ici tout va bien. Mais lorsque j’essaie d’utiliser la bibliothèque partagée en python, elle se déchire.

test.py

 from ctypes import byref, cdll, c_int import ctypes import numpy as np import cython l = cdll.LoadLibrary('absolute_path_to_so/libcluaf.so') a = np.arange(4, dtype=np.float64).reshape((2,2)) b = np.arange(4, dtype=np.float64).reshape((2,2)) l.multiply.argtypes = [ctypes.POINTER(ctypes.c_float), ctypes.c_int, ctypes.c_int, ctypes.POINTER(ctypes.c_float), ctypes.c_int, ctypes.c_int] a_list = [] b_list = [] for i in range(a.shape[0]): for j in range(a.shape[1]): a_list.append(a[i][j]) for i in range(b.shape[0]): for j in range(b.shape[1]): b_list.append(b[i][j]) arr_a = (ctypes.c_float * len(a_list))() arr_b = (ctypes.c_float * len(b_list))() l.multiply(arr_a, ctypes.c_int(2), ctypes.c_int(2), arr_b, ctypes.c_int(2), ctypes.c_int(2)) 

Je cours:

 python test.py 

et le résultat est:

 error: error loading module 'libpaths' from file '/usr/local/lib/lua/5.1/libpaths.so': /usr/local/lib/lua/5.1/libpaths.so: undefined symbol: lua_gettop 

J’ai cherché cette erreur ici et partout sur le Web, mais ils suggèrent (1) d’inclure -Wl, -E pour exporter des symboles ou (2) d’append des dépendances sur les liens, ce que j’ai fait. (1) J’ai -Wl, -E mais il semble ne rien faire. (2) J’ai inclus les dépendances (-L / usr / local / lib -lluajit -lluaT -lTH -lm -ldl)

Le test python échoue non pas lors de l’importation de la bibliothèque partagée, mais lors de l’appel de ‘require torch’ dans lua. C’est aussi la différence dans ce cas des autres cas que j’ai trouvés.

luajit.so définit le symbole lua_gettop (nm /usr/local/lib/luajit.so pour vérifier que) lua.h définit LUA_API int (lua_gettop) (lua_State * L);

J’imagine que quand comstackr c en binary tout fonctionne, car il trouve tous les symboles dans lua.h, mais l’utilisation de la bibliothèque partagée ne sélectionne pas lua_gettop dans luajit.so (je ne sais pas pourquoi).

www.luajit.org/running.html indique: ‘Sur la plupart des systèmes basés sur ELF (tels que Linux), vous devez exporter explicitement les symboles globaux lors de la liaison de votre application, par exemple avec: -Wl, -E require () essaie de charger les fonctions intégrées. les données de bytecode des symboles exportés (dans * .exe ou lua51.dll sous Windows) et des bibliothèques partagées dans package.cpath. ‘

package.cpath et package.path sont:

 ./?.so;/usr/local/lib/lua/5.1/?.so;/usr/local/lib/lua/5.1/loadall.so ./?.lua;/usr/local/share/luajit-2.0.2/?.lua;/usr/local/share/lua/5.1/?.lua;/usr/local/share/lua/5.1/?/init.lua 

Voici ce que nm libcluaf.so renvoie:

 00000000002020a0 B __bss_start 00000000002020a0 b completed.6972 w __cxa_finalize@@GLIBC_2.2.5 0000000000000a50 t deregister_tm_clones 0000000000000ac0 t __do_global_dtors_aux 0000000000201dd8 t __do_global_dtors_aux_fini_array_entry 0000000000202098 d __dso_handle 0000000000201de8 d _DYNAMIC 00000000002020a0 D _edata 00000000002020a8 B _end 0000000000000d28 T _fini 0000000000000b00 t frame_dummy 0000000000201dd0 t __frame_dummy_init_array_entry 0000000000000ed0 r __FRAME_END__ 0000000000202000 d _GLOBAL_OFFSET_TABLE_ w __gmon_start__ 0000000000000918 T _init w _ITM_deregisterTMCloneTable w _ITM_registerTMCloneTable 0000000000201de0 d __JCR_END__ 0000000000201de0 d __JCR_LIST__ w _Jv_RegisterClasses U lua_getfield 0000000000000d99 R luaJIT_BC_tensor U luaL_loadfile U luaL_newstate U luaL_openlibs U lua_pcall U lua_settop U luaT_newmetatable U lua_tolssortingng U luaT_pushudata U luaT_toudata U lua_type 0000000000000b35 T multiply U printf@@GLIBC_2.2.5 0000000000000a80 t register_tm_clones U THFloatStorage_newWithData U THFloatTensor_newWithStorage2d 00000000002020a0 d __TMC_END__ 

Merci d’avance

Sous Linux, les modules Lua ne sont pas directement liés à la bibliothèque Lua, mais s’attendent plutôt à retrouver les fonctions de l’API Lua déjà chargées. Cela se fait généralement en les exportant à partir de l’interpréteur en utilisant l’ -Wl,-E éditeur de liens -Wl,-E . Cet indicateur ne fonctionne que pour les symboles des exécutables , pas pour les bibliothèques partagées. Pour les bibliothèques partagées, il existe quelque chose de similaire: l’indicateur RTLD_GLOBAL pour la fonction dlopen . Par défaut, toutes les bibliothèques partagées répertoriées sur la ligne de commande du compilateur sont chargées à l’aide de RTLD_LOCAL , mais heureusement, Linux reprend les RTLD_LOCAL bibliothèque déjà ouverts. Donc vous pouvez soit:

Préchargez la bibliothèque Lua (JIT) à l’aide de RTLD_GLOBAL avant son chargement automatique (ce qui se produit lorsque vous chargez libcluaf.so ):

 from ctypes import byref, cdll, c_int import ctypes lualib = ctypes.CDLL("libluajit-5.1.so", mode=ctypes.RTLD_GLOBAL) l = cdll.LoadLibrary('absolute_path_to_so/libcluaf.so') # ... 

Vous pouvez également modifier les indicateurs de la bibliothèque Lua (JIT) par la suite, à l’aide de l’indicateur dlopen pour dlopen . Cet indicateur n’est pas dans POSIX cependant, et vous devrez probablement utiliser C pour le faire. Voir par exemple ici .

Le lua_gettop est une fonction définie dans le Lua .so, qui dans votre cas doit être luajit.so. On dirait que vous associez votre bibliothèque à celle-ci, ce qui est bien, puis vous liez main à votre bibliothèque. Le compilateur c suppose donc que les fonctions Lua utilisées par main dans le luajit. Jusqu’ici tout va bien.

Maintenant, lorsque vous chargez votre bibliothèque via ctypes en python, la bibliothèque luajit est-elle automatiquement chargée? Vous vous y attendez, mais vous devez confirmer, vous devez peut-être indiquer à ctypes de charger les bibliothèques liées. Une autre possibilité est que ctypes ou le chargeur de lib ne trouve pas luajit, peut-être parce qu’il cherche dans des endroits où luajit n’est pas situé. Pour être sûr, vous voudrez peut-être essayer de placer toutes les bibliothèques dans le même dossier à partir duquel vous appelez Python.

Si cela ne vous aide pas, essayez une variante de ce que vous avez essayé: ne chargez pas votre module en python, mais chargez directement luajit à l’aide de ctypes, et essayez d’appeler certaines de ses méthodes.

Pour échanger des données entre python / numpy et lua / torch, vous pouvez essayer une bibliothèque appelée ” lutorpie “. Il fait exactement ce que vous essayez de faire, partagez la mémoire et ne passez que le pointeur avec la méthode “asNumpyArray ()”.

 import lutorpy as lua import numpy as np ## run lua code in python with minimal modification: replace ":" to "._" t = torch.DoubleTensor(10,3) print(t._size()) # the corresponding lua version is t:size() ## convert torch tensor to numpy array ### Note: the underlying object are sharing the same memory, so the conversion is instant arr = t.asNumpyArray() print(arr.shape) ## or, you can convert numpy array to torch tensor xn = np.random.randn(100) ## convert the numpy array into torch tensor xt = torch.fromNumpyArray(xn)