-
Notifications
You must be signed in to change notification settings - Fork 36
Description
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;
}
}