Loading src/posix/udp.cpp +16 −15 Original line number Diff line number Diff line Loading @@ -414,21 +414,22 @@ ssize_t udp::sendBatch( } if (uniform) { // Build one big buffer size_t total_bytes = 0; for (auto& [p, l] : datagrams) total_bytes += l; std::vector<uint8_t> coalesced(total_bytes); size_t off = 0; for (auto& [p, l] : datagrams) { std::memcpy(coalesced.data() + off, p, l); off += l; // F21: one iovec per datagram, pointing straight at each // caller-owned buffer — no intermediate "coalesced" heap // allocation + memcpy of the whole batch. sendmsg()'s own // copy_from_iter() into the outgoing skb already walks a // multi-entry iovec exactly like a single contiguous buffer // (that's what iovec/scatter-gather I/O is for), and UDP_SEGMENT // segments the *logical* byte stream described by the iovec // array as a whole — it does not require the source bytes to // already be contiguous in user space. This removes one full // copy of every batch's total bytes on the GSO path. std::vector<struct iovec> iov(count); for (size_t i = 0; i < count; ++i) { iov[i].iov_base = const_cast<uint8_t*>(datagrams[i].first); iov[i].iov_len = datagrams[i].second; } struct iovec iov; iov.iov_base = coalesced.data(); iov.iov_len = coalesced.size(); // cmsg carrying UDP_SEGMENT size union { char buf[CMSG_SPACE(sizeof(uint16_t))]; Loading @@ -440,8 +441,8 @@ ssize_t udp::sendBatch( msg.msg_name = dest; msg.msg_namelen = dest_len; } msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_iov = iov.data(); msg.msg_iovlen = iov.size(); msg.msg_control = cmsg_buf.buf; msg.msg_controllen = sizeof(cmsg_buf.buf); Loading Loading
src/posix/udp.cpp +16 −15 Original line number Diff line number Diff line Loading @@ -414,21 +414,22 @@ ssize_t udp::sendBatch( } if (uniform) { // Build one big buffer size_t total_bytes = 0; for (auto& [p, l] : datagrams) total_bytes += l; std::vector<uint8_t> coalesced(total_bytes); size_t off = 0; for (auto& [p, l] : datagrams) { std::memcpy(coalesced.data() + off, p, l); off += l; // F21: one iovec per datagram, pointing straight at each // caller-owned buffer — no intermediate "coalesced" heap // allocation + memcpy of the whole batch. sendmsg()'s own // copy_from_iter() into the outgoing skb already walks a // multi-entry iovec exactly like a single contiguous buffer // (that's what iovec/scatter-gather I/O is for), and UDP_SEGMENT // segments the *logical* byte stream described by the iovec // array as a whole — it does not require the source bytes to // already be contiguous in user space. This removes one full // copy of every batch's total bytes on the GSO path. std::vector<struct iovec> iov(count); for (size_t i = 0; i < count; ++i) { iov[i].iov_base = const_cast<uint8_t*>(datagrams[i].first); iov[i].iov_len = datagrams[i].second; } struct iovec iov; iov.iov_base = coalesced.data(); iov.iov_len = coalesced.size(); // cmsg carrying UDP_SEGMENT size union { char buf[CMSG_SPACE(sizeof(uint16_t))]; Loading @@ -440,8 +441,8 @@ ssize_t udp::sendBatch( msg.msg_name = dest; msg.msg_namelen = dest_len; } msg.msg_iov = &iov; msg.msg_iovlen = 1; msg.msg_iov = iov.data(); msg.msg_iovlen = iov.size(); msg.msg_control = cmsg_buf.buf; msg.msg_controllen = sizeof(cmsg_buf.buf); Loading