Speeding up a loop (assembly)?

Posted on
  • I want to stream a waveform from an external SPI flash RAM (Winbond). Once the RAM has been prepared, reading back sequential data into is just a loop of SPI2.send(0) instructions whose return values are sequentially written into a uInt8Array.

    As mentioned in the docs, a function containing SPI calls cannot be compiled... how can I speed it up?

  • Easiest way is to get a single SPI.send command to transfer a big chunk of data...

    var sendData = new Uint8Array(256);
    sendData.fill(0);
    // now in the loop:
    var result = SPI2.send(sendData);
    // result will have 256 bytes in it
    
  • You could also include any required SPI commands at the beginning of the sendData array, and could then use ArrayBufferViews to cut out the data you need without a copy:

    var sendData = new Uint8Array(4+256);
    sendData.fill(0);
    sendData[0] = ...;
    sendData[1] = ...;
    sendData[2] = ...;
    sendData[3] = ...;
    // now in the loop:
    var result = SPI2.send(sendData);
    var data = new Uint8Array(sendData.buffer,4,256);
    
  • ... and you can write that new array into another array with a simple arrayTo.set(arrayFrom)

  • ...bothers me that there is still no better solution for this pattern. I suggested a while ago to use the same pattern for SPI.send() as provided for SPI.write(), where an object can be passed which includes the data and a repetition count. That way, no extra memory has to be wasted to just keep the peripheral sending... @Gordon, what's the constraint that does not allow something like that?

  • Cool, I didn't know it was that simple (and that spi.send can return arrays). But if I understand correctly, there is no way to shovel the data from SPI directly into the buffer of the waveform (waveforms bring their own buffers), so I always have to copy it? And I also have to temporarily allocate the space for the outgoing zeros, so effectively I need 3 times as much memory as the received payload?

  • I suggested a while ago to use the same pattern for SPI.send() as provided for SPI.write()

    It tries to return a buffer of the same type as the arguments, so it's not quite as easy. It is possible though - I've just had other things on my mind and this is firmly in the 'optimisation' pile of things :)

    there is no way to shovel the data from SPI directly into the buffer of the waveform (waveforms bring their own buffers), so I always have to copy

    Well, there's waveform.buffer.set(dataFromSPI), which is quite fast.

    I need 3 times as much memory as the received payload?

    If you don't absolutely need zeros, you could just pass the waveform's buffer in?

  • @Gordon, good to know... and I know very well that there are priorities... ;-) In case you get there - even when later than sooner - choosing a reasonable default is good enough for to get started (usually UInt8). After some thinking, the passed object could pass the constructor in addition to data and repeat. As far as I see, @Dennis got it working, and small buffers render better results than large one and the extra memory to drive the sending is not a primary killer. I'm not surprised about the smaller buffer, because garbage collection is one of the primary challenge for real time things... I know that Espruino has a very smart memory management and thus an affordable garbage collect... I noticed though hick-ups when driving steppers (continuously)... which is time sensitive kind of waves a real time thing too.

  • Oh, garbage collection... yes, that could be the reason for some oddities.

    @Gordon, one little tweak with (hopefully) a big impact could be to change the SPI.write function so that you can pass a target buffer to it and it will write the results into that buffer instead of returning a new one.

  • ...or buffer object, that would hold information about last (read and) written byte. Withe the read information the buffer can become cyclic.

  • I'd been wondering about that. I wonder whether thats almost another function call that's needed though? I think it would be hard to implement in a nice way with the existing SPI.send or SPI.write?

  • The existing functions could take additional optional arguments, and they could do the same as before if these arguments are omitted.

    I'm not so much in favor of having a multitude of functions doing essentially the same thing in different ways; I find flexible calling patterns more elegant. But it's not that important, and if dedicated functions are more efficient, maybe that's the way to go.

  • I like the idea of being able to give it a buffer to fill too (or to have it send stuff from a buffer, while stuffing the response data into the same buffer?)

  • Yes - the send+receive into the same buffer seems like a great idea really. Nice and flexible.

  • Post a reply
    • Bold
    • Italics
    • Link
    • Image
    • List
    • Quote
    • code
    • Preview
About

Speeding up a loop (assembly)?

Posted by Avatar for Dennis @Dennis

Actions