question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Slow performance reading/writing characteristics after idling

See original GitHub issue
  • bleak version: 0.9.1
  • Python version: 3.8.5
  • Operating System: Windows 10 v2004 (build 19041.508)

Description

I’m seeing strange performance issues using Bleak to read & write GATT characteristics on a BLE device, whereby a single read or write can take some 1400-2000ms to complete. It’s strange because it appears to be somewhat dependent on the process using Bleak going idle (as in, it’s much more likely to happen if the process was idle beforehand). This is a problem as it adds a huge artificial delay onto almost every attempt to communicate briefly with the device (since in almost every case, the BLE framework was idle prior to deciding to communicate for whatever reason).

In other words, if the read/write ops are chained together (one immediately after another) then the first one often seems to take a long time (1400-2000ms typically), and the rest blast through within some 30-40ms each.

And if time.sleep() is used prior to any read or write, even just for a handful of milliseconds, then more often than not, every subsequent read or write op will also take 1400-2000ms, not just the first one.

It’s as if something is going to sleep during idle periods and then taking ages to “wake up” again afterwards, but if it’s already “awake” then it responds instantly…

What I Did

I can’t really provide a reproducible example, but essentially, the test involves writing to a characteristic, then reading back a result from another characteristic from the same service, and doing this multiple times to read different data (the write tells the device what data we want to see in the subsequent read).

    async def __gatt_performance_test(self, address):
        if address in self.connected_devices:
            pass
        else: 
            await self.__connect_device(address)
        sv_uuid = "" # service UUID
        cc_char = "" # command characteristic UUID (what we write to)
        cd_char = "" # data characteristic UUID (what we read from)
        protocol_reg = "41" # data we're trying to request
        firmware_reg = "37"
        model_reg = "31"
        # spam BLE traffic; each __write and __read measures the time taken
        for i in range(0,100): 
            await self.__write_gatt_characteristic(address, sv_uuid, cc_char, "98"+protocol_reg) # this will typically be very slow on first iteration, but not always
            # time.sleep(0.03)
            await self.__read_gatt_characteristic(address, sv_uuid, cd_char) # fast if time.sleep() isn't used in between
            # time.sleep(0.001)
            await self.__write_gatt_characteristic(address, sv_uuid, cc_char, "98"+firmware_reg)
            # time.sleep(0.03)
            await self.__read_gatt_characteristic(address, sv_uuid, cd_char)
            # time.sleep(0.001)
            await self.__write_gatt_characteristic(address, sv_uuid, cc_char, "98"+model_reg)
            # time.sleep(0.03)
            await self.__read_gatt_characteristic(address, sv_uuid, cd_char)

And how the times are extracted (read_gatt_char is measured the same):

ts = time.time()
await device_client.write_gatt_char(characteristic_uuid, ba_out, True)
es = time.time() - ts # this is the time value we're looking at; anywhere from ~30ms to ~2000ms depending

Does this seem at all familiar to anyone or is it specific to my situation?

Things I’ve eliminated so far:

  • Not to do with range; the device is right next to the BLE modem
  • Not to do with modem hardware; tried using different modems
  • Not to do with the device I’m connecting to (at least, not entirely! Via an Android app performing the same test, performance is consistently much better)

Issue Analytics

  • State:closed
  • Created 3 years ago
  • Comments:9 (3 by maintainers)

github_iconTop GitHub Comments

1reaction
dlechcommented, Oct 7, 2021

Thanks for following up.

1reaction
NbrTDBcommented, Dec 1, 2020

I would love to, but alas, I can’t make any changes to the peripheral to run such tests haha!

I did acquire a BLE sniffer and Wireshark to have a look what was going on there. Though it didn’t seem as if Windows were trying to pick the absolute fastest parameters, they do seem to fall within the window provided by the device, and it did indeed ask the device for its preferred connection parameters before issuing the LL_CONNECTION_UPDATE_IND request. Thereafter, the device was reliably responding within about 15ms to transmissions from the PC.

On initial connection:

Link Layer Data
    Access Address: 0xc5353047
    CRC Init: 0x969411
    Window Size: 1 (1.25 msec)
    Window Offset: 36 (45 msec)
    Interval: 48 (60 msec)
    Latency: 0
    Timeout: 960 (9600 msec)
    Channel Map: ffffffff1f
    ...0 1101 = Hop: 13
    001. .... = Sleep Clock Accuracy: 151 ppm to 250 ppm (1)

And a few seconds later, having gathered preferred parameters:

Bluetooth Low Energy Link Layer
    Access Address: 0xc5353047
    Data Header: 0x0c0f
    Control Opcode: LL_CONNECTION_UPDATE_IND (0x00)
    Window Size: 1
    Window Offset: 0
    Interval: 12  << 15ms if I'm not mistaken! (12*1.25)
    Latency: 100
    Timeout: 1250
    Instant: 72
    CRC: 0x0da5c4

The data stream didn’t reveal anything (that I could see) suggesting a reason for any delays. Transmissions were promptly replied to by the device in keeping with the 15ms negotiated connection interval. But there were notable gaps in time for transmissions from the master (the PC) once connection, GATT/GAP interrogation and link negotiation were complete and communication could properly begin, which seemed to tie up with the aforementioned observations on Windows (as if Windows is just sitting there for a second or so before sending any requests to access the GATT services).

I have not managed to track down a version of Windows prior to 2004 but I have just been offered an update to 20H2, as well as (apparently) some updated Bluetooth drivers for my motherboard (though I doubt the latter will have any effect when this issue is replicated across different makes/models of BLE hardware and different Windows machines). So I’ll update all of that and have another crack at it!

Read more comments on GitHub >

github_iconTop Results From Across the Web

Idle Read After Write - IRAW - USENIX
In this paper, we present a trace-driven evaluation of IRAW and show its feasibility. Our analysis indicates that idleness is present in disk...
Read more >
Espresso idling resources - Android Developers
An idling resource represents an asynchronous operation whose results affect subsequent operations in a UI test.
Read more >
5 Ways to Fix Hard Drive Read Speed Slow Error - EaseUS
Low Read and Write Disk Speeds (Windows 10) ; Fix 2. Uninstall Unwanted Programs, Click "Start > Control Panel". Select "Programs and Features".....
Read more >
Idle Until Urgent — A web performance experiment - Sherry Hsu
C. Render carousels when the thread is idle or when they are scrolled into view (React-onVisible and Idle Queue).
Read more >
How to Fix Slow-Running Macs - The Mac Security Blog - Intego
There is a software problem. The operating system or specific applications are not working as they should. When troubleshooting a Mac, you go ......
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found