From f306149e3bd85db55d24ff20065eea233bd9129d Mon Sep 17 00:00:00 2001 From: David Mollitor Date: Wed, 16 Sep 2026 13:11:31 +0000 Subject: [PATCH] [SPARK-59707][CORE] Use ArrayDeque instead of LinkedList in TransportFrameDecoder `TransportFrameDecoder.buffers` holds the input `ByteBuf`s not yet consumed into a frame and is used purely as a FIFO queue: `add` at the tail, `getFirst`/`removeFirst` at the head, plus iteration and `clear`. It was a `LinkedList`, which allocates a node object on every `add`. `channelRead` runs on every inbound read of every connection, so that is a steady per-read allocation on a hot path. Switch it to `ArrayDeque`, which offers the same operations with identical FIFO semantics but reuses a small preallocated backing array (the queue is typically just one buffer), avoiding the per-read node allocation and improving locality. This mirrors SPARK-59431, which made the same change for `BytesToBytesMap.dataPages`. No behavior change; `TransportFrameDecoderSuite` passes. Co-authored-by: Isaac --- .../apache/spark/network/util/TransportFrameDecoder.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/common/network-common/src/main/java/org/apache/spark/network/util/TransportFrameDecoder.java b/common/network-common/src/main/java/org/apache/spark/network/util/TransportFrameDecoder.java index 6b490068507aa..c920167a5c281 100644 --- a/common/network-common/src/main/java/org/apache/spark/network/util/TransportFrameDecoder.java +++ b/common/network-common/src/main/java/org/apache/spark/network/util/TransportFrameDecoder.java @@ -17,7 +17,7 @@ package org.apache.spark.network.util; -import java.util.LinkedList; +import java.util.ArrayDeque; import com.google.common.annotations.VisibleForTesting; import io.netty.buffer.ByteBuf; @@ -50,7 +50,9 @@ public class TransportFrameDecoder extends ChannelInboundHandlerAdapter { private static final int UNKNOWN_FRAME_SIZE = -1; private static final long CONSOLIDATE_THRESHOLD = 20 * 1024 * 1024; - private final LinkedList buffers = new LinkedList<>(); + // Pending input buffers not yet consumed into a frame. ArrayDeque avoids a + // per-channelRead list-node allocation; used strictly FIFO. + private final ArrayDeque buffers = new ArrayDeque<>(); private final ByteBuf frameLenBuf = Unpooled.buffer(LENGTH_SIZE, LENGTH_SIZE); private final long consolidateThreshold;