Skip to content

high peak RSS while delivering binary messages #128

Description

@r2evans

(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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions