Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

High-level API introduces large and unpredictable performance penalty #5

Copy link
Copy link
@malsyned

Description

@malsyned
Issue body actions

I'm using usb4java 1.2.0 with a full-speed USB device that I'm developing.

When using the low-level libusb API, I am able to achieve throughput speeds near the theoretical maximum for a full-speed bulk endpoint (1114.112 KB/s).

If I switch to the high-level javax.usb API, my throughput drops by an amount that appears to depend not just on the machine I'm using, but on the port I'm connected to. I always see performance penalties of at least 50%.

I've developed a simple benchmarking tool. The device side reads a 4-byte network-byte-order integer from an OUT endpoint, then transmits that many bytes of garbage on an IN endpoint.

The low-level and high-level usb4java benchmark code is inlined below.

On one port on my development machine, I get the following results:

org.usb4java API benchmark
Writing header
4 bytes sent
Reading 1048576 bytes
1048576 bytes received
1048576 bytes in 1021 ms, 1002.9382957884428 KB/s

javax.usb API benchmark
Writing header
4 bytes sent
Reading 1048576 bytes
1048576 bytes received
1048576 bytes in 2311 ms, 443.09822587624404 KB/s

On another port, I get these results:

org.usb4java API benchmark
Writing header
4 bytes sent
Reading 1048576 bytes
1048576 bytes received
1048576 bytes in 971 ms, 1054.5829042224511 KB/s

javax.usb API benchmark
Writing header
4 bytes sent
Reading 1048576 bytes
1048576 bytes received
1048576 bytes in 16538 ms, 61.91800701414923 KB/s

As you can see, the performance hit is 56% on one port, and 94%(!) on the other.

I have seen this performance penalty on Windows 7 64-bit and Ubuntu Linux 32-bit and 64-bit.

UsbBench.java:

import org.usb4java.*;
import java.nio.*;
import java.io.*;

public class UsbBench
{
    public static final short idVendor = (short)0x03eb;
    public static final short idProduct = (short)0x2423;
    public static final byte ifaceId = (byte)0x00;
    public static final byte outEp = (byte)0x02;
    public static final byte inEp = (byte)0x81;
    public static final int timeout = 60000;

    public static void main(String args[])
        throws Exception
    {
        int result;
        DeviceHandle handle;
        int size = Integer.parseInt(args[0]);

        ByteArrayOutputStream headerStream = new ByteArrayOutputStream();
        DataOutputStream out = new DataOutputStream(headerStream);
        out.writeInt(size);
        byte[] header = headerStream.toByteArray();

        result = LibUsb.init(null);
        if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to initialize libusb.", result);
        handle = findDevice(idVendor, idProduct);
        if (handle == null) throw new RuntimeException("Device not found");

        try {
            result = LibUsb.claimInterface(handle, ifaceId);
            if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to claim interface", result);

            ByteBuffer buffer = ByteBuffer.allocateDirect(header.length);
            buffer.put(header);
            IntBuffer transfered = IntBuffer.allocate(1);

            System.out.printf("Writing header\n");
            result = LibUsb.bulkTransfer(handle, outEp, buffer, transfered, timeout); 
            if (result != LibUsb.SUCCESS) throw new LibUsbException("Bulk transfer failed", result);
            System.out.printf("%d bytes sent\n", transfered.get());

            buffer = ByteBuffer.allocateDirect(size);
            transfered = IntBuffer.allocate(1);

            System.out.printf("Reading %d bytes\n", size);
            long start = System.currentTimeMillis();
            result = LibUsb.bulkTransfer(handle, inEp, buffer, transfered, timeout); 
            long time = System.currentTimeMillis() - start;
            if (result != LibUsb.SUCCESS) throw new LibUsbException("Bulk transfer failed", result);
            int rx = transfered.get();
            System.out.printf("%d bytes received\n", rx);

            System.out.printf("%s bytes in %s ms, %s KB/s\n",
                              rx, time, (rx / (time / 1000.0)) / 1024.0);
        }
        finally
        {
            LibUsb.releaseInterface(handle, ifaceId);
            LibUsb.close(handle);
            LibUsb.exit(null);
        }
    }

    private static DeviceHandle findDevice(short vendorId, short productId)
    {
        // Read the USB device list
        DeviceList list = new DeviceList();
        int result = LibUsb.getDeviceList(null, list);
        if (result < 0) throw new LibUsbException("Unable to get device list", result);

        try
        {
            // Iterate over all devices and scan for the right one
            for (Device device: list)
            {
                DeviceDescriptor descriptor = new DeviceDescriptor();
                result = LibUsb.getDeviceDescriptor(device, descriptor);
                if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to read device descriptor", result);
                if (descriptor.idVendor() == vendorId && descriptor.idProduct() == productId)
                {
                    DeviceHandle handle = new DeviceHandle();
                    result = LibUsb.open(device, handle);
                    if (result != LibUsb.SUCCESS) throw new LibUsbException("Unable to open USB device", result);
                    return handle;
                }
            }
        }
        finally
        {
            // Ensure the allocated device list is freed
            LibUsb.freeDeviceList(list, true);
        }

        // Device not found
        return null;
    }
}

UsbBenchJavax:

import javax.usb.*;
import java.io.*;
import java.util.*;

public class UsbBenchJavax
{
    public static final short idVendor = (short)0x03eb;
    public static final short idProduct = (short)0x2423;
    public static final byte ifaceId = (byte)0x00;
    public static final byte outEp = (byte)0x02;
    public static final byte inEp = (byte)0x81;

    public static void main(String[] args)
        throws Exception
    {
        int size = Integer.parseInt(args[0]);

        ByteArrayOutputStream headerStream = new ByteArrayOutputStream();
        DataOutputStream out = new DataOutputStream(headerStream);
        out.writeInt(size);
        byte[] header = headerStream.toByteArray();

        UsbServices services = UsbHostManager.getUsbServices();
        UsbDevice usbdev = findDevice(services.getRootUsbHub(),
                                      idVendor, idProduct);
        UsbConfiguration config = usbdev.getActiveUsbConfiguration();
        UsbInterface iface = config.getUsbInterface(ifaceId);
        UsbPipe inPipe = iface.getUsbEndpoint(inEp).getUsbPipe();
        UsbPipe outPipe = iface.getUsbEndpoint(outEp).getUsbPipe();
        iface.claim();
        inPipe.open();
        outPipe.open();

        System.out.printf("Writing header\n");
        int tx = outPipe.syncSubmit(header);
        System.out.printf("%d bytes sent\n", tx);

        byte[] buffer = new byte[size];
        System.out.printf("Reading %d bytes\n", size);
        long start = System.currentTimeMillis();
        int rx = inPipe.syncSubmit(buffer);
        long time = System.currentTimeMillis() - start;
        System.out.printf("%d bytes received\n", rx);

        System.out.printf("%s bytes in %s ms, %s KB/s\n",
                          rx, time, (rx / (time / 1000.0)) / 1024.0);
    }

    @SuppressWarnings("unchecked")
    private static UsbDevice findDevice(UsbHub hub,
                                        short idVendor, short idProduct)
        throws UsbException
    {
        for (UsbDevice device : (List<UsbDevice>) hub.getAttachedUsbDevices())
        {
            UsbDeviceDescriptor desc = device.getUsbDeviceDescriptor();
            if (desc.idVendor() == idVendor && desc.idProduct() == idProduct)
                return device;

            if (device.isUsbHub())
            {
                device = findDevice((UsbHub) device, idVendor, idProduct);
                if (device != null) return device;
            }
        }
        return null;
    }
}
Reactions are currently unavailable

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      Morty Proxy This is a proxified and sanitized view of the page, visit original site.