<div dir="ltr"><br><div class="gmail_extra"><br><br><div class="gmail_quote">On Wed, Jul 17, 2013 at 10:39 AM, Nathaniel Smith <span dir="ltr"><<a href="mailto:njs@pobox.com" target="_blank">njs@pobox.com</a>></span> wrote:<br>

<blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-left-style:solid;padding-left:1ex"><div class=""><div class="h5">On Tue, Jul 16, 2013 at 7:53 PM, Frédéric Bastien <<a href="mailto:nouiz@nouiz.org">nouiz@nouiz.org</a>> wrote:<br>


> Hi,<br>
><br>
><br>
> On Tue, Jul 16, 2013 at 11:55 AM, Nathaniel Smith <<a href="mailto:njs@pobox.com">njs@pobox.com</a>> wrote:<br>
>><br>
>> On Tue, Jul 16, 2013 at 2:34 PM, Arink Verma <<a href="mailto:arinkverma@gmail.com">arinkverma@gmail.com</a>> wrote:<br>
>>><br>
>>> >Each ndarray does two mallocs, for the obj and buffer. These could be<br>
>>> > combined into 1 - just allocate the total size and do some pointer<br>
>>> > >arithmetic, then set OWNDATA to false.<br>
>>> So, that two mallocs has been mentioned in project introduction. I got<br>
>>> that wrong.<br>
>><br>
>><br>
>> On further thought/reading the code, it appears to be more complicated<br>
>> than that, actually.<br>
>><br>
>> It looks like (for a non-scalar array) we have 2 calls to PyMem_Malloc: 1<br>
>> for the array object itself, and one for the shapes + strides. And, one call<br>
>> to regular-old malloc: for the data buffer.<br>
>><br>
>> (Mysteriously, shapes + strides together have 2*ndim elements, but to hold<br>
>> them we allocate a memory region sized to hold 3*ndim elements. I'm not sure<br>
>> why.)<br>
>><br>
>> And contrary to what I said earlier, this is about as optimized as it can<br>
>> be without breaking ABI. We need at least 2 calls to malloc/PyMem_Malloc,<br>
>> because the shapes+strides may need to be resized without affecting the much<br>
>> larger data area. But it's tempting to allocate the array object and the<br>
>> data buffer in a single memory region, like I suggested earlier. And this<br>
>> would ALMOST work. But, it turns out there is code out there which assumes<br>
>> (whether wisely or not) that you can swap around which data buffer a given<br>
>> PyArrayObject refers to (hi Theano!). And supporting this means that data<br>
>> buffers and PyArrayObjects need to be in separate memory regions.<br>
><br>
><br>
> Are you sure that Theano "swap" the data ptr of an ndarray? When we play<br>
> with that, it is on a newly create ndarray. So a node in our graph, won't<br>
> change the input ndarray structure. It will create a new ndarray structure<br>
> with new shape/strides and pass a data ptr and we flag the new ndarray with<br>
> own_data correctly to my knowledge.<br>
><br>
> If Theano pose a problem here, I'll suggest that I fix Theano. But currently<br>
> I don't see the problem. So if this make you change your mind about this<br>
> optimization, tell me. I don't want Theano to prevent optimization in NumPy.<br>
<br>
</div></div>It's entirely possible I misunderstood, so let's see if we can work it<br>
out. I know that you want to assign to the ->data pointer in a<br>
PyArrayObject, right? That's what caused some trouble with the 1.7 API<br>
deprecations, which were trying to prevent direct access to this<br>
field? Creating a new array given a pointer to a memory region is no<br>
problem, and obviously will be supported regardless of any<br>
optimizations. But if that's all you were doing then you shouldn't<br>
have run into the deprecation problem. Or maybe I'm misremembering!<br></blockquote><div><br></div><div style>What is currently done at only 1 place is to create a new PyArrayObject with a given ptr. So NumPy don't do the allocation. We later change that ptr to another one.</div>

<div style><br></div><div style>It is the change to the ptr of the just created PyArrayObject that caused problem with the interface deprecation. I fixed all other problem releated to the deprecation (mostly just rename of function/macro). But I didn't fixed this one yet. I would need to change the logic to compute the final ptr before creating the PyArrayObject object and create it with the final data ptr. But in call cases, NumPy didn't allocated data memory for this object, so this case don't block your optimization.</div>

<div style><br></div><div style>One thing in our optimization "wish list" is to reuse allocated PyArrayObject between Theano function call for intermediate results(so completly under Theano control). This could be useful in particular for reshape/transpose/subtensor. Those functions are pretty fast and from memory, I already found the allocation time was significant. But in those cases, it is on PyArrayObject that are views, so the metadata and the data would be in different memory region in all cases.</div>

<div style><br></div><div style>The other cases of optimization "wish list"  is if  we want to reuse the PyArrayObject when the shape isn't the good one (but the number of dimensions is the same). If we do that for operation like addition, we will need to use PyArray_Resize(). This will be done on PyArrayObject whose data memory was allocated by NumPy. So if you do one memory allowcation for metadata and data, just make sure that PyArray_Resize() will handle that correctly.</div>

<div style><br></div><div>On the usefulness of doing only 1 memory allocation, on our old gpu ndarray, we where doing 2 alloc on the GPU, one for metadata and one for data. I removed this, as this was a bottleneck. allocation on the CPU are faster the on the GPU, but this is still something that is slow except if you reuse memory. Do <span style="color:rgb(80,0,80)">PyMem_Malloc, reuse previous small allocation?</span></div>

<div style><br></div><div style>For those that read up all this, the conclusion is that Theano should block this optimization. If you optimize the allocation of new PyArrayObject, they will be less incentive to do the "wish list" optimization.</div>

<div style><br></div><div style><span style="color:rgb(80,0,80)">One last thing to keep in mind is that you should keep the data segment aligned. I would arg that alignment on the datatype size isn't enough, so I would suggest on cache line size or something like this. But I don't have number to base this one. This would also help in the case of resize that change the number of dimensions.</span></div>

<div style><span style="color:rgb(80,0,80)"><br></span></div><div style><span style="color:rgb(80,0,80)"><br>Fred</span></div><div style><span style="color:rgb(80,0,80)"><br></span></div></div></div></div>