Merge pull request #171 from mathieucarbou/buffcredit

in-flight buffer credits
This commit is contained in:
Mathieu Carbou
2024-12-15 18:33:25 +01:00
committed by GitHub
7 changed files with 48 additions and 13 deletions

View File

@@ -55,7 +55,7 @@ jobs:
- name: Install AsyncTCP (ESP32)
if: ${{ matrix.core == 'esp32:esp32' }}
run: ARDUINO_LIBRARY_ENABLE_UNSAFE_INSTALL=true arduino-cli lib install --git-url https://github.com/mathieucarbou/AsyncTCP#v3.2.15
run: ARDUINO_LIBRARY_ENABLE_UNSAFE_INSTALL=true arduino-cli lib install --git-url https://github.com/mathieucarbou/AsyncTCP#v3.3.0
- name: Install ESPAsyncTCP (ESP8266)
if: ${{ matrix.core == 'esp8266:esp8266' }}

View File

@@ -80,8 +80,8 @@ lib_deps = mathieucarbou/ESPAsyncWebServer @ 3.4.0
**Dependencies:**
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.2.15`
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.15](https://github.com/mathieucarbou/AsyncTCP/releases)
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.3.0`
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.3.0](https://github.com/mathieucarbou/AsyncTCP/releases)
- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
@@ -99,7 +99,7 @@ AsyncTCPSock can be used instead of AsyncTCP by excluding AsyncTCP from the libr
lib_compat_mode = strict
lib_ldf_mode = chain
lib_deps =
; mathieucarbou/AsyncTCP @ 3.2.15
; mathieucarbou/AsyncTCP @ 3.3.0
https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
mathieucarbou/ESPAsyncWebServer @ 3.4.0
lib_ignore =
@@ -116,7 +116,7 @@ Performance of `mathieucarbou/ESPAsyncWebServer @ 3.4.0`:
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
```
With `mathieucarbou/AsyncTCP @ 3.2.15`
With `mathieucarbou/AsyncTCP @ 3.3.0`
[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)

View File

@@ -80,8 +80,8 @@ lib_deps = mathieucarbou/ESPAsyncWebServer @ 3.4.0
**Dependencies:**
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.2.15`
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.2.15](https://github.com/mathieucarbou/AsyncTCP/releases)
- **ESP32 with AsyncTCP**: `mathieucarbou/AsyncTCP @ 3.3.0`
Arduino IDE: [https://github.com/mathieucarbou/AsyncTCP#v3.3.0](https://github.com/mathieucarbou/AsyncTCP/releases)
- **ESP32 with AsyncTCPSock**: `https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip`
@@ -99,7 +99,7 @@ AsyncTCPSock can be used instead of AsyncTCP by excluding AsyncTCP from the libr
lib_compat_mode = strict
lib_ldf_mode = chain
lib_deps =
; mathieucarbou/AsyncTCP @ 3.2.15
; mathieucarbou/AsyncTCP @ 3.3.0
https://github.com/mathieucarbou/AsyncTCPSock/archive/refs/tags/v1.0.3-dev.zip
mathieucarbou/ESPAsyncWebServer @ 3.4.0
lib_ignore =
@@ -116,7 +116,7 @@ Performance of `mathieucarbou/ESPAsyncWebServer @ 3.4.0`:
> autocannon -c 10 -w 10 -d 20 http://192.168.4.1
```
With `mathieucarbou/AsyncTCP @ 3.2.15`
With `mathieucarbou/AsyncTCP @ 3.3.0`
[![](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)](https://mathieu.carbou.me/ESPAsyncWebServer/perf-c10.png)

View File

@@ -28,7 +28,7 @@
{
"owner": "mathieucarbou",
"name": "AsyncTCP",
"version": "^3.2.15",
"version": "^3.3.0",
"platforms": "espressif32"
},
{

View File

@@ -31,7 +31,7 @@ lib_deps =
; bblanchon/ArduinoJson @ 5.13.4
; bblanchon/ArduinoJson @ 6.21.5
bblanchon/ArduinoJson @ 7.2.1
mathieucarbou/AsyncTCP @ 3.2.15
mathieucarbou/AsyncTCP @ 3.3.0
board = esp32dev
board_build.partitions = partitions-4MB.csv
board_build.filesystem = littlefs
@@ -49,7 +49,7 @@ platform = https://github.com/pioarduino/platform-espressif32/releases/download/
; board = esp32-s3-devkitc-1
; board = esp32-c6-devkitc-1
lib_deps =
mathieucarbou/AsyncTCP @ 3.2.15
mathieucarbou/AsyncTCP @ 3.3.0
[env:arduino-310]
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip
@@ -102,7 +102,7 @@ board = ${sysenv.PIO_BOARD}
platform = https://github.com/pioarduino/platform-espressif32/releases/download/51.03.05/platform-espressif32.zip
board = ${sysenv.PIO_BOARD}
lib_deps =
mathieucarbou/AsyncTCP @ 3.2.15
mathieucarbou/AsyncTCP @ 3.3.0
[env:ci-arduino-310]
platform = https://github.com/pioarduino/platform-espressif32/releases/download/53.03.10-rc3/platform-espressif32.zip

View File

@@ -47,6 +47,10 @@ class AsyncBasicResponse : public AsyncWebServerResponse {
class AsyncAbstractResponse : public AsyncWebServerResponse {
private:
// amount of responce data in-flight, i.e. sent, but not acked yet
size_t _in_flight{0};
// in-flight queue credits
size_t _in_flight_credit{2};
String _head;
// Data is inserted into cache at begin().
// This is inefficient with vector, but if we use some other container,

View File

@@ -352,7 +352,21 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
request->client()->close();
return 0;
}
// return a credit for each chunk of acked data (polls does not give any credits)
if (len)
++_in_flight_credit;
// for chunked responses ignore acks if there are no _in_flight_credits left
if (_chunked && !_in_flight_credit) {
#ifdef ESP32
log_d("(chunk) out of in-flight credits");
#endif
return 0;
}
_ackedLength += len;
_in_flight -= (_in_flight > len) ? len : _in_flight;
// get the size of available sock space
size_t space = request->client()->space();
size_t headLen = _head.length();
@@ -364,16 +378,31 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
String out = _head.substring(0, space);
_head = _head.substring(space);
_writtenLength += request->client()->write(out.c_str(), out.length());
_in_flight += out.length();
--_in_flight_credit; // take a credit
return out.length();
}
}
if (_state == RESPONSE_CONTENT) {
// for response data we need to control the queue and in-flight fragmentation. Sending small chunks could give low latency,
// but flood asynctcp's queue and fragment socket buffer space for large responses.
// Let's ignore polled acks and acks in case when we have more in-flight data then the available socket buff space.
// That way we could balance on having half the buffer in-flight while another half is filling up, while minimizing events in asynctcp q
if (_in_flight > space) {
// log_d("defer user call %u/%u", _in_flight, space);
// take the credit back since we are ignoring this ack and rely on other inflight data
if (len)
--_in_flight_credit;
return 0;
}
size_t outLen;
if (_chunked) {
if (space <= 8) {
return 0;
}
outLen = space;
} else if (!_sendContentLength) {
outLen = space;
@@ -422,6 +451,8 @@ size_t AsyncAbstractResponse::_ack(AsyncWebServerRequest* request, size_t len, u
if (outLen) {
_writtenLength += request->client()->write((const char*)buf, outLen);
_in_flight += outLen;
--_in_flight_credit; // take a credit
}
if (_chunked) {