Efficient conversion of ByteArray to byte[]

Is there a way to efficiently get the bytes from a ByteArray?

I'm working on an app that handles a data stream from an USB device. The ByteArray.Get() method takes between 10 and 60ms while it only takes 1ms to transfer the same amount of bytes throw USB.

A Buffer.BlockCopy() for the same amount of memory takes less than 0.1ms.

I already tried using ByteArray.Wrap() but the pre-allocated byte[] is not updated.

I was previously using UsbConnection.BulkTransfer() for the synchronous USB transfer but wasn't achieving the required performance. Now I'm using the asynchronous UsbRequest.Queue() and getting a much better performance. Unfortunately it doesn't handle byte[] directly and ByteArray.Get() performance is awful... :(

Antao

Posts

  • JeremyKolbJeremyKolb USMember ✭✭✭

    I hit a similar issue with marshaling byte[] through a content provider and couldn't figure it out (the java -> c# wrapper does a bunch of copies of the underlying buffer which is where you lose the performance).

  • AntaoAlmadaAntaoAlmada PTMember ✭✭

    Sorry all but I meant ByteBuffer not ByteArray... :/

    I coded a custom JNI wrapper for the ByteBuffer.Get() method and found that the culprit is the JNIEnv.NewArray () method. The JNIEnv.CallNonvirtualObjectMethod() and JNIEnv.CopyArray () methods are actually very fast.

            static IntPtr byteBufferClassRef;
            static IntPtr byteBufferGetBII;
    
            static void Get (ByteBuffer byteBuffer, byte[] dst, int dstOffset, int byteCount)
            {
                if (byteBufferClassRef == IntPtr.Zero) {
                    byteBufferClassRef = JNIEnv.FindClass("java/nio/ByteBuffer");
                }
                if (byteBufferGetBII == IntPtr.Zero) {
                    byteBufferGetBII = JNIEnv.GetMethodID (byteBufferClassRef, "get", "([BII)Ljava/nio/ByteBuffer;");
                }
                var dstArray = JNIEnv.NewArray (dst);
                JNIEnv.CallNonvirtualObjectMethod (
                    byteBuffer.Handle, 
                    byteBufferClassRef, 
                    byteBufferGetBII, 
                    new JValue (dstArray),
                    new JValue (dstOffset),
                    new JValue (byteCount));
                JNIEnv.CopyArray (dstArray, dst);
                JNIEnv.DeleteLocalRef (dstArray);
            }
    

    I researched some more on the ByteBuffer and now I'm looking into using ByteBuffer.AllocateDirect() that allocates directly in native memory. Does anybody know if there's some way to access this memory?

  • AntaoAlmadaAntaoAlmada PTMember ✭✭

    I GOT IT! Lightning speed! ;)

    I'm now allocating using ByteBuffer.AllocateDirect() and copying into my byte[] buffer using:

    Marshal.Copy(byteBuffer.GetDirectBufferAddress(), buffer, offset, bytesRead);
    
  • JeremyKolbJeremyKolb USMember ✭✭✭

    Interesting. Can you post the full snippet? What's the difference in speed?

  • AntaoAlmadaAntaoAlmada PTMember ✭✭

    Here is a small snippet:

        const int bufferSize = 4096;
        var buffer = new byte[bufferSize];
        var byteBuffer = ByteBuffer.AllocateDirect (bufferSize);
        Marshal.Copy(byteBuffer.GetDirectBufferAddress(), buffer, 0, bufferSize);
    

    The allocation is slow but the copy is very fast. It should be only used when you have large and long lived buffers, my case exactly.

    The copy of 4096 bytes now takes 0.0305ms instead of the previous 60ms...

  • BrendanZagaeskiBrendanZagaeski USForum Administrator, Xamarin Team Xamurai

    In case anyone comes across this thread first while researching this issue, I have now filed a bug report that summarizes Antao's nice findings from this thread, adds one additional possible workaround, and includes a bit of extra explanation:

    https://bugzilla.xamarin.com/show_bug.cgi?id=31260

Sign In or Register to comment.