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 combiningFallbackclass andRecursive_attrclass. - Requesting NumPy/CuPy modules without calling.
- Raising
TypeErrorif module is called. - Handling of NumPy scalars such as
numpy.nan,numpy.pietc. - Adding
__repr__inRecursive_attrclass. - Bringing behaviour of
fallback_modecloser to actual NumPy/CuPy.