[io] Improve scheduling of IO worker threads

The allocation of the at most 32 IO worker threads to IO requests
includes an inefficient check that a thread is free, when a request
completes.

A server application was taking 5% of its time running this check,
which is a .contains call on HashMap.values.

This CL replaces it with a use count on each IO thread, which is
checked against 0.

Bug: https://github.com/dart-lang/sdk/issues/50907
Change-Id: I6d13f9c62d36c34f165b2b4e32a48caeb6f9a77b
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/278503
Reviewed-by: Zach Anderson <zra@google.com>
Commit-Queue: William Hesse <whesse@google.com>
This commit is contained in:
William Hesse 2023-01-06 15:47:57 +00:00 committed by Commit Queue
parent 112e1213dc
commit e8283b0839

View file

@ -8,35 +8,34 @@ class _IOServicePorts {
// We limit the number of IO Service ports per isolate so that we don't
// spawn too many threads all at once, which can crash the VM on Windows.
static const int maxPorts = 32;
List<SendPort> _ports = <SendPort>[];
List<SendPort> _freePorts = <SendPort>[];
Map<int, SendPort> _usedPorts = new HashMap<int, SendPort>();
final List<SendPort> _ports = [];
final List<int> _useCounts = [];
final List<int> _freePorts = [];
final Map<int, int> _usedPorts = HashMap<int, int>();
_IOServicePorts();
SendPort _getPort(int forRequestId) {
if (_freePorts.isEmpty && _usedPorts.length < maxPorts) {
assert(!_usedPorts.containsKey(forRequestId));
if (_freePorts.isEmpty && _ports.length < maxPorts) {
final SendPort port = _newServicePort();
_ports.add(port);
_freePorts.add(port);
_useCounts.add(0);
_freePorts.add(_ports.length - 1);
}
if (!_freePorts.isEmpty) {
final SendPort port = _freePorts.removeLast();
assert(!_usedPorts.containsKey(forRequestId));
_usedPorts[forRequestId] = port;
return port;
}
// We have already allocated the max number of ports. Re-use an
// existing one.
final SendPort port = _ports[forRequestId % maxPorts];
_usedPorts[forRequestId] = port;
return port;
// Use a free port if one exists.
final index = _freePorts.isNotEmpty
? _freePorts.removeLast()
: forRequestId % maxPorts;
_usedPorts[forRequestId] = index;
_useCounts[index]++;
return _ports[index];
}
void _returnPort(int forRequestId) {
final SendPort port = _usedPorts.remove(forRequestId)!;
if (!_usedPorts.values.contains(port)) {
_freePorts.add(port);
final index = _usedPorts.remove(forRequestId)!;
if (--_useCounts[index] == 0) {
_freePorts.add(index);
}
}