Compared with PJSIP
Same Asterisk, same captures, one after the other.
The results
- About 72 KB per call. Ten thousand concurrent calls with audio both ways, held in one process, set up at 499 a second.
- Under a quarter of pjsua's memory. Registered and idle, 2.8 MB of private memory against 12.7 MB; with four calls up, 3.1 MB against 16.8 MB.
- Under an eighth of the CPU. Four calls up: 2.3 % of a core against 20.15 %.
- A smaller INVITE, never fragmented. 869 bytes against 1373; a request past 1300 bytes goes over TCP, as RFC 3261 §18.1.1 requires.
- A call that follows its own address. Re-registered and re-offered from a new network with nobody telling it to.
-
Private memory, registered and idle
2.8 MB12.7 MB -
Private memory, four calls up
3.1 MB16.8 MB -
CPU, four calls up, share of one core
2.3 %20.15 % -
First INVITE on the wire, no ICE
869 bytes1373 bytes
Ten thousand calls in one process
Two processes on one machine, every call G.711 over a UDP socket of its own with a tone both ways, placed at 500 a second and held sixty seconds. About 72 KB of memory and a thousandth of a core per call carrying audio, with more than half of the signalling thread's time still to spare at ten thousand calls.
| This end, over the hold | 5 000 calls | 10 000 calls |
|---|---|---|
| Calls up | 5 000 | 10 000 |
| Setup rate | 500 a second | 499 a second |
| Setup, INVITE to 2xx: p50, p90 | 2.6 ms, 6.3 ms | 7.5 ms, 95.6 ms |
| Processor | 5.32 cores | 8.48 cores |
| Resident memory, peak | 363 MB | 718 MB |
Memory and CPU
Per call above idle, Sipral used 0.41 % of a core at four calls and 0.28 % at a hundred, and its memory grew by about 76 kB a call from ten to a hundred. The private column is the one to compare: it leaves out shared-library pages.
| Calls up | Sipral RSS / private | Sipral CPU | pjsua RSS / private | pjsua CPU |
|---|---|---|---|---|
| idle | 4.6 MB / 2.8 MB | 0.65 % | 13.3 MB / 12.7 MB | 0.65 % |
| 1 | 4.7 MB / 2.9 MB | 1.40 % | 14.2 MB / 13.7 MB | 6.55 % |
| 4 | 4.9 MB / 3.1 MB | 2.30 % | 17.3 MB / 16.8 MB | 20.15 % |
| 10 | 5.4 MB / 3.5 MB | 4.35 % | 4 up of 10: 17.4 MB / 16.9 MB | 22.70 % |
| 100 | 12.2 MB / 10.3 MB | 28.65 % | 4 up of 100: 17.3 MB / 16.7 MB | 20.95 % |
The INVITE on the wire
Sipral offers its default catalogue (Opus, G.722, G.711, telephone-event) and one ICE candidate per stream, with RTCP multiplexed on the RTP port. RFC 3261 §18.1.1 sends a request over 1300 bytes over a congestion-controlled transport when the path MTU is unknown; Sipral's core holds to that and asks the application for TCP, so its INVITE is never split into IP fragments that a NAT or firewall may drop.
| Offer | Sipral | pjsua |
|---|---|---|
| Every default codec, no ICE | 869 bytes | 1373 bytes |
| Every default codec, ICE | 1034 bytes, 1 candidate; with credentials 1333 bytes, not sent over UDP | 1570 bytes, 2 candidates, sent over UDP as two IP fragments; call set up in 6.1 ms |
| G.711 alone, ICE | 954 bytes, 1 candidate; call set up in 9.2 ms | 1164 bytes, 2 candidates; call set up in 4.1 ms |
A moved address
One call up, then the client taken off the network and put back at a different address, as a laptop or a phone changes networks. Sipral notices the move itself: it binds its signalling to the new address, re-registers and offers every call again from there.
| From the moment the new address existed | Sipral | pjsua, left alone | pjsua, told |
|---|---|---|---|
| First SIP from the new address | 394 ms, a REGISTER | never | 123 ms, a re-INVITE |
| First audio sent from there | 3 ms | 17 ms | 1 ms |
| First audio received there again | 417 ms | never | 126 ms |
| Call still up after 20 s | yes | yes, one way | yes |
Registering and calling
From the first request to the 200 that accepted it, each client challenged once by Asterisk.
| Measured | Sipral | pjsua |
|---|---|---|
| Registration, first REGISTER to its 200 | 7.7 ms | 10.3 ms |
| Outgoing call to Asterisk echo, first INVITE to its 200 | 10.8 ms | 4.6 ms |
| Incoming call from Asterisk, INVITE to the client 200 | 9.5 ms | 4.2 ms |
| First INVITE, no ICE, size on the wire | 869 bytes | 1373 bytes |
A bad link
One call held 30 seconds over each impairment profile, applied both ways with tc netem. Each end's view of the audio it received, every row rated by the same E-model (ITU-T G.107, G.711 with loss concealment). Under the same loss, Asterisk rated what the two clients sent alike.
| Profile | Measured at | Received | Loss | Jitter | RTT | R | MOS |
|---|---|---|---|---|---|---|---|
| lossy | Asterisk, from Sipral | 1402 | 7.09 % | 9.0 ms | 73 ms | 70.5 | 3.62 |
| lossy | Sipral | 1395 | 8.40 % | 10.4 ms | 103 ms | 67.2 | 3.46 |
| lossy | Asterisk, from pjsua | 1410 | 7.18 % | 9.0 ms | 85 ms | 70.1 | 3.60 |
| lossy | pjsua | 1.4K | 7.2 % | 8.9 ms | 87 ms | 70.1 | 3.60 |
| mobile | Asterisk, from Sipral | 1442 | 4.50 % | 35.0 ms | 119 ms | 75.2 | 3.83 |
| mobile | Sipral | 1455 | 4.34 % | 52.6 ms | 154 ms | 71.6 | 3.67 |
| mobile | Asterisk, from pjsua | 1465 | 3.04 % | 22.0 ms | 126 ms | 79.9 | 4.02 |
| mobile | pjsua | 1.5K | 3.5 % | 18.7 ms | 99 ms | 79.0 | 3.99 |
| satellite | Asterisk, from Sipral | 1521 | 0.46 % | 13.0 ms | 505 ms | 71.0 | 3.64 |
| satellite | Sipral | 1553 | 0.64 % | 15.3 ms | 504 ms | 69.8 | 3.59 |
| satellite | Asterisk, from pjsua | 1550 | 0.58 % | 10.0 ms | 488 ms | 72.5 | 3.71 |
| satellite | pjsua | 1.6K | 0.4 % | 8.8 ms | 485 ms | 73.7 | 3.76 |
| Profile | netem, both directions |
|---|---|
| lossy | delay 40ms 15ms loss gemodel 4% 40% 60% 2% reorder 1% 30% |
| mobile | delay 60ms 30ms distribution normal loss gemodel 2% 40% 60% 1% |
| satellite | delay 250ms 10ms distribution normal loss 0.5% |
Methodology
- Date
- 29 September 2026
- Host
- Debian 13, Linux 6.12.95, 32 cores, x86-64, a virtual machine doing other work at the same time
- PBX
- Asterisk 22.10.1, one account, G.711 only, one contact at a time
- Sipral
- The headless agent example, release build, Rust 1.95.0, in debian:trixie-slim
- PJSIP
- pjsua 2.17 from Alpine 3.24 (package pjsua-2.17-r0), run as shipped
- Timing
- read off a packet capture taken in each client's own network namespace
- Memory, CPU
- resident and private memory from smaps_rollup, user plus system time over 20 seconds, the same /proc files for both
- Calls
- originated by Asterisk to a cadenced tone, G.711 both ways, echoed back
- Impairment
- tc netem in both directions; every row rated by one reduced ITU-T G.107 E-model
- Settings
- pjsua run with --max-calls=4, the limit its packaged build was compiled with; the Sipral agent with --invite-burst 200, its scanner guard raised for a PBX that offers a hundred calls within a second
- Scale run
- Intel Xeon E5-2698 v4 at 2.2 GHz, 32 vCPUs, Debian 13, kernel 6.12; 29 September 2026
The whole comparison is one script in the Sipral source tree, published together with the source, so anyone can run it again and compare their own numbers.
Sipral is built to replace PJSIP in products that cannot take on the GPL: it contains no GPL or LGPL code, and its documentation maps each pjsua concept to its equivalent. For a closed-source product, see licensing.
Ship SIP in a closed app, without the GPL.
One price per company, perpetual for the versions delivered during maintenance, app stores allowed. Ask privately.
Request a quote