blob: c714ff641a2dda56adb7cf187365e7f8b8191999 [file] [log] [blame] [view]
Fabien Sanglard2cbf5912024-10-07 17:18:35 -07001# Delayed ACK
2
3Historically, ADB transport protocol transfer speed was affected by two factors.
4
51. Each `A_WRTE` apacket was CRCed upon write and the CRC was checked upon read on the other end.
62. There could be only one `A_WRTE` apacket in-flight on an asocket. A local asocket
7would not schedule more data to be sent out until it had received an `A_OKAY` apacket response from
8its peer.
9
10The first issue was solved in [aosp/568123](https://android-review.googlesource.com/q/568123).
11In that CL, the protocol was updated to remove the requirement for CRC generation and verification.
12This does not affect the reliability of a transport since both USB and TCP have packet checksums of their own.
13
14The second issue is solved by "delayed ACK" ([aosp/1953877](https://android-review.googlesource.com/q/1953877)),
Fabien Sanglard2d3e62c2025-02-06 13:45:48 -080015an experimental feature controlled by the environment variable `ADB_BURST_MODE`.
Fabien Sanglard2cbf5912024-10-07 17:18:35 -070016
17# How delayed ACK works
18
19The idea is to introduce the concept of a per-asocket "available send bytes" (ASB) integer.
20This integer represent how many bytes we are willing to send without having received any
21`A_OKAY` for them.
22
23While the ASB is positive, the asocket does not wait for an `A_OKAY` before sending
24more `A_WRTE` apackets. A remote asocket can be written to up until the ASB is exhausted.
25
26The ASB capability is first negotiated on `A_OPEN`/`A_OKAY` exchange. After
27that, the ASB is maintained via decrement upon `A_WRTE` and increment
28upon `A_OKAY`.
29
30This approach allows to "burst" `A_WRTE` packet but also "burst" `A_OKAY` packets
31to allow several `A_WRTE` packets to be in-flight on an asocket. This greatly
32increases data transfer throughput.
33
34# Implementation
35
36## Packet update
371. `A_OPEN` unused field (`arg1`) is repurposed to declare the wish to use delayed ACK features.
38If not supported, the receiving end of the `A_OPEN` will `A_CLSE` the connection.
392. `A_OKAY` now has a payload (a int32_t) which acknowledge how much payload was
40received in the last received `A_WRTE` apacket.
41
42## Trace
43
44Here are two traces showing the timing of three A_WRTE.
45
46### Before
47```
48Host > A_OPEN > Device
49Host > A_WRTE > Device
50The LS removes itself from the fdevent EPOLLIN and nothing is sent.
51Host < A_OKAY < Device
52The LS requests fdevent EPOLLIN for its fd to start reading and send more A_WRTE.
53Host > A_WRTE > Device
54The LS removes itself from the fdevent EPOLLIN and nothing is sent.
55Host < A_OKAY < Device
56The LS requests fdevent EPOLLIN for its fd to start reading and send more A_WRTE.
57Host > A_WRTE > Device
58The LS removes itself from the fdevent EPOLLIN and nothing is sent.
59Host < A_OKAY < Device
60The LS requests fdevent EPOLLIN for its fd to start reading and send more A_WRTE.
61```
62
63
64## After
65
66With ASB, see how `A_WRTE` and `A_OKAY` are burst instead of being paired.
67
68```
69Host(ASB=0) > A_OPEN(arg1=1MiB) > Device
70Host(ASB=X) < A_OKAY(<ASB=X>) < Device
71Host<ASB=X-a) > A_WRTE(payload size=a) > Device
72Host<ASB=Y-a-b) > A_WRTE(payload size=b) > Device
73Host<ASB=Z-a-b-c) > A_WRTE(payload size=c) > Device
74ASB is < 0. The LS removes itself from the fdevent EPOLLIN and nothing is sent.
75...
76Host(ASB=X-b-c) < A_OKAY(<a>) < Device
77ASB is > 0. The LS requests fdevent EPOLLIN for its fd to start reading and send more A_WRTE.
78...
79Host(ASB=X-c) < A_OKAY(<b>) < Device
80Host(ASB=X) < A_OKAY(<c>) < Device
81```
82
Fabien Sanglardbfc72642025-02-06 14:23:51 -080083# Results
Fabien Sanglard2cbf5912024-10-07 17:18:35 -070084
Fabien Sanglardbfc72642025-02-06 14:23:51 -080085Initial testing show that Burst Mode is nearly 70% faster at pushing files to a device over a USB-3 cable.
86
87## Before
88```
89$ adb kill-server && unset ADB_BURST_MODE && adb start-server
90$ adb push -Z ~/Desktop/10G1 /data/local/tmp
91/usr/local/google/home/sanglardf/Desktop/10G1: 1 file pushed, 0 skipped. 202.0 MB/s (10737418240 bytes in 50.701s)
92$ adb push -Z ~/Desktop/10G1 /data/local/tmp
93/usr/local/google/home/sanglardf/Desktop/10G1: 1 file pushed, 0 skipped. 205.9 MB/s (10737418240 bytes in 49.724s)
94$ adb push -Z ~/Desktop/10G1 /data/local/tmp
95/usr/local/google/home/sanglardf/Desktop/10G1: 1 file pushed, 0 skipped. 197.6 MB/s (10737418240 bytes in 51.828s)
96```
97
98## After
99
100```
101$ adb kill-server && export ADB_BURST_MODE=1 && adb start-server
102$ adb push -Z ~/Desktop/10G1 /data/local/tmp
103/usr/local/google/home/sanglardf/Desktop/10G1: 1 file pushed, 0 skipped. 337.2 MB/s (10737418240 bytes in 30.365s)
104$ adb push -Z ~/Desktop/10G1 /data/local/tmp
105/usr/local/google/home/sanglardf/Desktop/10G1: 1 file pushed, 0 skipped. 342.0 MB/s (10737418240 bytes in 29.945s)
106$ adb push -Z ~/Desktop/10G1 /data/local/tmp
107/usr/local/google/home/sanglardf/Desktop/10G1: 1 file pushed, 0 skipped. 341.3 MB/s (10737418240 bytes in 30.000s)
108```