Skip to content

numpy scalar * array-like == performance horror #3375

Closed

Description

@inducer

When I run this:

import numpy as np

class MyThing(object):
    def __init__(self, shape):
        self.shape = shape

    def __len__(self):
        return self.shape[0]

    def __getitem__(self, i):
        if not isinstance(i, tuple):
            i = (i,)
        if len(i) > len(self.shape):
            raise IndexError("boo")

        return MyThing(self.shape[len(i):])

    def __rmul__(self, other):
        print "RMUL"
        return self

print np.float64(5)*MyThing((3,3))

I get this:

RMUL
RMUL
RMUL
RMUL
RMUL
RMUL
RMUL
RMUL
RMUL
[[<__main__.MyThing object at 0x2298b90>
  <__main__.MyThing object at 0x2298bd0>
  <__main__.MyThing object at 0x2298c10>]
 [<__main__.MyThing object at 0x2298c50>
  <__main__.MyThing object at 0x2298c90>
  <__main__.MyThing object at 0x2298cd0>]
 [<__main__.MyThing object at 0x2298d10>
  <__main__.MyThing object at 0x2298d50>
  <__main__.MyThing object at 0x2298d90>]]

Is there a way to tell numpy, "no, don't worry about it, just call __rmul__ on the whole thing, instead of picking it apart?"

In my specific case, MyThing is an array-like object that lives on a GPU, and while it's possible (and not necessarily incorrect) to pick the array apart in this way, it's unexpected and has really terrible performance.

(sorry about the many edits)

Activity

  1. seberg commented on May 28, 2013

    @seberg
    Member

    If you define an __array_priority__ probably it does. Other then that this is the completly correct behaviour since if something appears to be not array aware (and this has no array priority/array attribute to indicate something like that), it needs to convert to an array first. And doing the calculation elementwise for object arrays is the correct thing to do.

    Your object is a python sequence it is not something that indicates it knows about arrays to numpy. Numpy can convert it, but it cannot do any assumptions about it. It should be sufficient to define __array_priority__. There may be improvements possible in how numpy does these things, but this is not a numpy issue as is.

  2. inducer commented on May 28, 2013

    @inducer
    ContributorAuthor

    __array_priority__ fixes this. Thanks!

  3. inducer commented on Jun 30, 2013

    @inducer
    ContributorAuthor

    Actually, I spoke too soon. This here:

    import numpy as np
    
    class MyThing(object):
        __array_priority__ = 1000
    
        def __init__(self, shape):
            self.shape = shape
    
        def __len__(self):
            return self.shape[0]
    
        def __getitem__(self, i):
            print "GETITEM", i
            if not isinstance(i, tuple):
                i = (i,)
            if len(i) > len(self.shape):
                raise IndexError("boo")
    
            return MyThing(self.shape[len(i):])
    
        def __rmul__(self, other):
            print "RMUL"
            return self
    
    print np.float64(5)*MyThing((3,3))
    

    prints

    GETITEM 0
    GETITEM 0
    GETITEM 0
    GETITEM 0
    GETITEM 1
    GETITEM 2
    GETITEM 1
    GETITEM 2
    GETITEM 0
    GETITEM 0
    GETITEM 1
    GETITEM 2
    GETITEM 1
    GETITEM 0
    GETITEM 1
    GETITEM 2
    GETITEM 2
    GETITEM 0
    GETITEM 1
    GETITEM 2
    RMUL
    

    so the np.float64*MyThing operation still leisurely iterates through the entire array before it decides, whoops, you know what, screw this, let's just call __rmul__. If the real-life (GPU-ly) equivalent of MyThing has a few million entries, that goes back to being a performance horror.

    (Forgot to say: I'd like to request that this bug be reopened.)

  4. seberg commented on Jun 30, 2013

    @seberg
    Member

    True, probably the check for array priority needs to be moved further to the front somewhere, this is not good. I guess the thing gets converted to an array, and only then it notices that the actual object knew what to do in the first place.

  5. reopened this on Jun 30, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions