(Same start as #126.)
Delta ~600 MB at the onMessage callback; removing the two avoidable copies measures ~400 MB — 2 payload-sized allocations wasted per message. Also: byte-by-byte copy loop with an int index (UB past 2 GB), and wsSend truncates lengths to int.
### R -q -f 04-binary-payload-triple-copy.R
library(websocket)
cat("websocket", as.character(packageVersion("websocket")),
"from", find.package("websocket"), "\n")
# websocket 1.4.4 from /Users/r2/Library/R/4.5/websocket
rss_mb <- function() {
as.numeric(system(paste("ps -o rss= -p", Sys.getpid()), intern = TRUE)) / 1024
}
PAYLOAD_MB <- 100
payload <- raw(PAYLOAD_MB * 1024^2)
srv <- new.env()
s <- httpuv::startServer("127.0.0.1", httpuv::randomPort(), list(
onWSOpen = function(ws) srv$ws <- ws
))
url <- paste0("ws://127.0.0.1:", s$getPort(), "/")
got <- new.env()
ws <- WebSocket$new(url, maxMessageSize = 512 * 1024^2)
ws$onMessage(function(e) {
got$rss_at_delivery <- rss_mb() # all copies are alive right now
got$size <- length(e$data)
})
while (ws$readyState() < 1L) later::run_now(0.05)
invisible(gc())
rss_before <- rss_mb()
srv$ws$send(payload)
t_start <- Sys.time()
while (is.null(got$size) && difftime(Sys.time(), t_start, units = "secs") < 60) {
later::run_now(0.1)
}
ws$close()
s$stop()
cat(sprintf("payload: %d MB, received: %d bytes\n", PAYLOAD_MB, got$size))
# payload: 100 MB, received: 104857600 bytes
cat(sprintf("RSS before send: %8.1f MB\n", rss_before))
# RSS before send: 192.1 MB
# [> cat(sprintf("RSS at onMessage delivery: %8.1f MB\n", got$rss_at_delivery))
# RSS at onMessage delivery: 792.2 MB
cat(sprintf("delta at delivery: %8.1f MB (payload is %d MB)\n",
got$rss_at_delivery - rss_before, PAYLOAD_MB))
# delta at delivery: 600.1 MB (payload is 100 MB)
# 2026-08-28 12:06:49] [error] handle_read_frame error: asio.system:54 (Connection reset by peer)
After a patch I'm working on, the 600.1MB reduces to 400MB.
On Ubuntu, the numbers are 393MB (before) and 194 (after).
(websocket-1.4.4 on in R-4.5.2, confirmed on both macos and ubuntu-24.04)
(Same start as #126.)
Delta ~600 MB at the
onMessagecallback; removing the two avoidable copies measures ~400 MB — 2 payload-sized allocations wasted per message. Also: byte-by-byte copy loop with anintindex (UB past 2 GB), andwsSendtruncates lengths toint.After a patch I'm working on, the 600.1MB reduces to 400MB.
On Ubuntu, the numbers are 393MB (before) and 194 (after).
(websocket-1.4.4 on in R-4.5.2, confirmed on both macos and ubuntu-24.04)