Page 1 of 1

Allocation when slicing memoryview instance

Posted: Tue Oct 16, 2018 12:12 pm
by pythoncoder
Am I suffering from brain-fade or is something wrong here? Tests done on a Pyboard using the onboard accelerometer as an I2C data source.

This sample works:

Code: Select all

from machine import I2C
from pyb import Timer
a = bytearray(2)
m = memoryview(a)
i = I2C(2)
def cb(_):
    i.readfrom_into(60, m)
t = Timer(1, freq=1, callback=cb)
but this produces a memory error:

Code: Select all

from machine import I2C
from pyb import Timer
a = bytearray(10)
m = memoryview(a)
i = I2C(2)
def cb(_):
    i.readfrom_into(60, m[0:2])
t = Timer(1, freq=1, callback=cb)
The callback works in that if I issue cb(0) at the REPL: the first two bytes of the 10 byte array are updated. A memoryview lets you take a slice into the original data without copying. So why is memory allocated?

Re: machine.I2C: allocation reading into pre-allocated buffer

Posted: Tue Oct 16, 2018 12:29 pm
by Roberthh
I am also confused by the follwing, which I stumbled over a few days (or weeks) ago, and which did not meet my expectation:

Code: Select all

>>> s="1234567890"
>>> m=memoryview(s)
>>> m[0:2]
<memoryview>
>>> m[0]
49
>>> x=m[0:2]
>>> x
<memoryview>
>>> s[0:2]
'12'
>>> for _ in m[0:2]:
...     _
...
49
50
I expeceted m[0:2] to return the same as s[0:2]. Iterating through m it is however possible, but not slicing.

Re: machine.I2C: allocation reading into pre-allocated buffer

Posted: Tue Oct 16, 2018 12:50 pm
by pythoncoder
A slice of a memoryview returns another memoryview. A memoryview instance doesn't have the methods of the object it's based on, so there aren't many things you can do with it. Thus

Code: Select all

>>> a = b'the quick brown fox'
>>> a.find(b'q')
4
>>> m = memoryview(a)
>>> m.find(b'q')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'memoryview' object has no attribute 'find'
>>> 
You can cast it to the original type, but then you're creating a copy which normally defeats the object.

Code: Select all

a = bytearray(20)
m = memoryview(a)
m1 = m[1:3]  # m1 is another memoryview instance. Still points into the original bytearray.
b = bytearray(m1)  # b has the attributes of a bytearray. But it's a new instance.
This is true for CPython and MicroPython.

This fails too. OK, I've got it.

Posted: Tue Oct 16, 2018 1:05 pm
by pythoncoder

Code: Select all

from machine import I2C
from pyb import Timer
a = bytearray(10)
m = memoryview(a)
b = bytearray((1,2))
def cb(_):
    m[3:5] = b
t = Timer(1, freq=1, callback=cb)
So the problem has nothing to do with I2C but with memoryview slicing.
[EDIT]
I've answered my own question: m[x:y] creates a new memoryview object on the heap. This implies it's impossible (in an interrupt context) to use I2C.readfrom_into to populate a circular buffer.

In any event I've come up with a better solution for my application.

Re: Allocation when slicing memoryview instance

Posted: Tue Oct 16, 2018 4:13 pm
by jickster
Until scoped allocation is implemented.


Sent from my iPhone using Tapatalk Pro

Re: Allocation when slicing memoryview instance

Posted: Tue Oct 16, 2018 5:45 pm
by pythoncoder
Indeed. It does seem unfortunate that memoryview objects in particular use the heap. I have raised this RFC with a minimal test case. I'll be interested to see the response.