2.6 KiB
freeswitch-0001 — mod_conference.c: relationship list scan inside per-sample audio mixing (O(M²·R·S))
Severity: CRITICAL
File: src/mod/applications/mod_conference/mod_conference.c
Lines: 617-673 (audio mixing loop)
Pattern
// Outer: for each output member
for (omember = conference->members; omember; omember = omember->next) {
// Middle: for each audio sample
for (x = 0; x < bytes / 2; x++) {
...
if (conference->relationship_total) {
// Inner: for each input member
for (imember = conference->members; imember; imember = imember->next) {
// Innermost: linear scan of singly-linked relationship list
for (rel = imember->relationships; rel; rel = rel->next) {
if (rel->id == omember->id || rel->id == 0) { ... break; }
}
if (!found) {
for (rel = omember->relationships; rel; rel = rel->next) {
if (rel->id == imember->id || rel->id == 0) { ... break; }
}
}
}
}
}
}
conference_relationship_t is a singly-linked list (mod_conference.h:770-774).
The innermost relationship scan is O(R) per (omember, imember, sample) triple.
Complexity
O(S × M × M × R) per mix cycle where:
- S = samples per frame (typically 160 at 8kHz/20ms)
- M = member count
- R = relationships per member
At M=20 members, S=160, R=5 relationships: 20×20×160×5 = 3,200,000 ops per mix cycle (50Hz). = 160M ops/sec just for relationship scanning, all inside a mutex-held loop.
Root Cause
The relationship check is nested inside the per-sample loop. The relationship result (can A hear B?) does not change per-sample — it only changes on relationship updates.
Fix
Pre-compute relationship matrix outside the sample loop:
Before for (x = 0; x < bytes/2; x++), build a boolean matrix or set:
// Per mixing frame (outside sample loop):
// For each (omember, imember) pair, check relationships once → store in a bitmask
// Then the per-sample loop just does: if (exclude_matrix[omember_idx][imember_idx]) z -= rptr[x];
Alternative: use switch_core_hash keyed by (omember->id << 32) | imember->id for O(1) lookup,
pre-computed at frame start, invalidated only on conference_member_add_relationship().
Speedup (estimated)
Relationship check moves from O(R) inside O(S×M²) to O(1) table lookup: ~500× at M=20, R=5. Full fix moves relationship resolution entirely outside the sample loop: O(S×M²) → O(M²+S×M).