Hey! So, this is the second blog-post in my series of GSoC experience. In this post I’ll be sharing what I did, what difficulties I came across in the first two weeks of Phase-1.
Actually, I had a pretty great start! I came to know the awesomeness of Python. I learnt many things, from Python internals to unittests. Also some git and GitHub things were a new experience to me. Time passed by and I did not even realize it.
First week
First week of my coding period was all about trial and error. I got to learn many things regarding Python internals. I played with __dir__
, __dict__
and many more. These also included Python magic methods such as __getattr__
, __getattribute__
, __setattr__
. These methods are super cool. They let you hack your way into Python internals. Python is such an awesome language!
I tried many different implementations to achieve the goal of capturing attributes. It was very easy to catch just one attribute i.e, capturing numpy.ones
, numpy.compress
was easy. But to take this work to next level was quite troublesome. It was needed for catching functions like numpy.linalg.norm
which included sub-modules of NumPy between them. I struggled with this a lot. I also tried using __setattr__
while exploring whole NumPy directory. But that lead to recursion error because of many sub-modules of NumPy having cyclic dependencies.
Second week
After trying many ideas, I finally got it to work. I implemented a class Recursive_attr
which had __getattr__
working in recursion to catch attributes and storing them in a list. This also worked out quite well for functions such as numpy.linalg.norm
which contained a sub-module.
I then had to implement a way to catch *args
and **kwargs
passed to the function. It was easy. Then I had to implement a way to search for method using previously prepared list in both CuPy and NumPy. And then calling that method with captured *args
, **kwargs
and by performing necessary data transfers from GPU to CPU and back to GPU after getting result. Then finally returning the result. I tested several functions with it. It seemed to work really well!
In this process I learnt many things including Python’s awesome memory allocation methods, automatic garbage collection and Python giving same ids to special immutable objects. How cool is that!
Summary
I implemented following classes/methods during this period. It’s in PR #2229.
- class
Recursive_attr
- class
FallbackUtil
- class
Fallback
- method
call_cupy
- method
call_numpy
- method
ram2vram
- method
vram2ram
Currently all basic methods for fallback_mode
has been implemented. There is still much left for improvement and some bug fixes. Still it’s able to show following behavior.
>>> from cupy.fallback_mode import numpy
>>> numpy.ones((3,4)) # calling function in cupy
array([[1., 1., 1., 1.],
[1., 1., 1., 1.],
[1., 1., 1., 1.]])
>>> x = numpy.array([1,2,3,4])
>>> type(x)
<class cupy.core.core.ndarray>
>>> numpy.argmin(x) # calling function in cupy
array(0, dtype=int64)
>>> type(numpy.argmin(x))
<class cupy.core.core.ndarray>
>>> x = numpy.array([float(nan),2,3,4])
>>> numpy.nanargmin(x) # calling function not in cupy
Attribute nanargmin not found in cupy. falling back to numpy
1
>>> numpy.linalg() # requesting module in cupy
<module cupy.linalg from \lib\\site-packages\\cupy\\linalg\\__init__.py>
>>> numpy.matrixlib() # requesting module not in cupy
Attribute matrixlib not found in cupy. falling back to numpy
<module numpy.matrixlib from \lib\\site-packages\\numpy\\matrixlib\\__init__.py>
>>>
Future work
Future work will consist of preparing tests for fallback_mode
. Plus, there are some improvements and bug fixes planned after discussing with mentor. These includes:
- Good class structure for
fallback_mode
. Probably combiningFallback
class andRecursive_attr
class. - Requesting NumPy/CuPy modules without calling.
- Raising
TypeError
if module is called. - Handling of NumPy scalars such as
numpy.nan
,numpy.pi
etc. - Adding
__repr__
inRecursive_attr
class. - Bringing behaviour of
fallback_mode
closer to actual NumPy/CuPy.