MINOR: connection: align toremove_{lock,connections} and cleanup into idle_conns
We used to have 3 thread-based arrays for toremove_lock, idle_cleanup,
and toremove_connections. The problem is that these items are small,
and that this creates false sharing between threads since it's possible
to pack up to 8-16 of these values into a single cache line. This can
cause real damage where there is contention on the lock.
This patch creates a new array of struct "idle_conns" that is aligned
on a cache line and which contains all three members above. This way
each thread has access to its variables without hindering the other
ones. Just doing this increased the HTTP/1 request rate by 5% on a
16-thread machine.
The definition was moved to connection.{c,h} since it appeared a more
natural evolution of the ongoing changes given that there was already
one of them declared in connection.h previously.
diff --git a/src/mux_h2.c b/src/mux_h2.c
index bec6af0..c6634c5 100644
--- a/src/mux_h2.c
+++ b/src/mux_h2.c
@@ -3524,13 +3524,13 @@
int ret = 0;
- HA_SPIN_LOCK(OTHER_LOCK, &toremove_lock[tid]);
+ HA_SPIN_LOCK(OTHER_LOCK, &idle_conns[tid].toremove_lock);
if (t->context == NULL) {
/* The connection has been taken over by another thread,
* we're no longer responsible for it, so just free the
* tasklet, and do nothing.
*/
- HA_SPIN_UNLOCK(OTHER_LOCK, &toremove_lock[tid]);
+ HA_SPIN_UNLOCK(OTHER_LOCK, &idle_conns[tid].toremove_lock);
tasklet_free(tl);
goto leave;
}
@@ -3547,7 +3547,7 @@
if (conn_in_list)
MT_LIST_DEL(&conn->list);
- HA_SPIN_UNLOCK(OTHER_LOCK, &toremove_lock[tid]);
+ HA_SPIN_UNLOCK(OTHER_LOCK, &idle_conns[tid].toremove_lock);
if (!(h2c->wait_event.events & SUB_RETRY_SEND))
ret = h2_send(h2c);
@@ -3643,15 +3643,15 @@
}
/* connections in error must be removed from the idle lists */
- HA_SPIN_LOCK(OTHER_LOCK, &toremove_lock[tid]);
+ HA_SPIN_LOCK(OTHER_LOCK, &idle_conns[tid].toremove_lock);
MT_LIST_DEL((struct mt_list *)&conn->list);
- HA_SPIN_UNLOCK(OTHER_LOCK, &toremove_lock[tid]);
+ HA_SPIN_UNLOCK(OTHER_LOCK, &idle_conns[tid].toremove_lock);
}
else if (h2c->st0 == H2_CS_ERROR) {
/* connections in error must be removed from the idle lists */
- HA_SPIN_LOCK(OTHER_LOCK, &toremove_lock[tid]);
+ HA_SPIN_LOCK(OTHER_LOCK, &idle_conns[tid].toremove_lock);
MT_LIST_DEL((struct mt_list *)&conn->list);
- HA_SPIN_UNLOCK(OTHER_LOCK, &toremove_lock[tid]);
+ HA_SPIN_UNLOCK(OTHER_LOCK, &idle_conns[tid].toremove_lock);
}
if (!b_data(&h2c->dbuf))
@@ -3721,7 +3721,7 @@
/* We're about to destroy the connection, so make sure nobody attempts
* to steal it from us.
*/
- HA_SPIN_LOCK(OTHER_LOCK, &toremove_lock[tid]);
+ HA_SPIN_LOCK(OTHER_LOCK, &idle_conns[tid].toremove_lock);
if (h2c && h2c->conn->flags & CO_FL_LIST_MASK)
MT_LIST_DEL(&h2c->conn->list);
@@ -3732,7 +3732,7 @@
if (!t->context)
h2c = NULL;
- HA_SPIN_UNLOCK(OTHER_LOCK, &toremove_lock[tid]);
+ HA_SPIN_UNLOCK(OTHER_LOCK, &idle_conns[tid].toremove_lock);
task_destroy(t);
@@ -3778,9 +3778,9 @@
}
/* in any case this connection must not be considered idle anymore */
- HA_SPIN_LOCK(OTHER_LOCK, &toremove_lock[tid]);
+ HA_SPIN_LOCK(OTHER_LOCK, &idle_conns[tid].toremove_lock);
MT_LIST_DEL((struct mt_list *)&h2c->conn->list);
- HA_SPIN_UNLOCK(OTHER_LOCK, &toremove_lock[tid]);
+ HA_SPIN_UNLOCK(OTHER_LOCK, &idle_conns[tid].toremove_lock);
/* either we can release everything now or it will be done later once
* the last stream closes.