specinferspeculative-decodingllm-servingtree-attentiongpu-optimizationinference-accelerationtutorial

SpecInfer Guide: Tree-Based Speculative LLM Serving

SpecInfer Architecture: Tree-Based Speculative Inference, Tree Attention, and Parallel Verification

The Memory-Bandwidth Wall and the Failure of Linear Speculation

In modern high-concurrency artificial intelligence infrastructure, automated data fetching workflows, and desktop intelligence assistants powered by AiFetchly, serving Large Language Models (LLMs) represents the primary computational and economic bottleneck. Whether generating complex code refactorings, extracting structured data through high-speed web scraping, or executing multi-step conversational reasoning, foundation models are constrained by the physical mechanics of autoregressive token generation.

During standard incremental decoding, generating a sequence of $N$ tokens requires executing $N$ distinct forward passes through the model’s parameters:

$$x_1 \to x_2 \to x_3 \to \dots \to x_N$$

Because batch-1 autoregressive decoding emits only a single token per forward step, it operates with extremely low arithmetic intensity (often less than $1.0\text{ FLOP/Byte}$). For a 70-billion parameter model in FP16 precision, the GPU must transfer approximately 140 GB of model weights from High-Bandwidth Memory (HBM) into on-chip cache registers just to generate one token. Even on premier hardware like the NVIDIA A100 or H100, the Tensor Cores sit over 80% idle while waiting on memory bus transfers.

To bypass this memory-bandwidth saturation, the machine learning community developed Speculative Decoding (Leviathan et al., Chen et al.). By pairing a small, fast Speculative Small Model (SSM) with a large target LLM, the system drafts $K$ candidate tokens sequentially and verifies all $K$ tokens simultaneously in a single forward pass of the target model.

However, conventional speculative decoding relies strictly on linear sequence speculation, which introduces a crippling architectural failure mode: the exponential acceptance cascade failure.

The Linear Speculation Dilemma: Draft Model (SSM) Proposes Linear Chain of 4 Tokens: [ t1: " The" ] ──► [ t2: " capital" ] ──► [ t3: " of" ] ──► [ t4: " France" ] │ │ │ │ ▼ ▼ ▼ ▼ Target LLM Verifies: t1 Match? YES! t2 Match? NO! [ REJECTED ] [ REJECTED ] (Target wants " city") (Discarded) (Discarded) │ ▼ Result: Only 1 token accepted! Tokens t3 and t4 were computed in vain.

1. The Exponential Decay of Linear Candidate Sequences

In linear speculation, a draft sequence is only as strong as its weakest link. If token $j$ in a speculative sequence of length $K$ is rejected by the target LLM, all subsequent speculated tokens ${j+1, \dots, K}$ are immediately invalidated and discarded, regardless of whether they would have been valid completions of the corrected prefix.

If an SSM has an average per-token agreement probability $p \approx 0.70$ with the target LLM:

  • Probability of accepting 1 token: $p^1 = 70.0%$
  • Probability of accepting 2 tokens: $p^2 = 49.0%$
  • Probability of accepting 4 tokens: $p^4 = 24.0%$
  • Probability of accepting 6 tokens: $p^6 = 11.8%$

As the speculative chain grows, the probability of full acceptance drops exponentially. Consequently, naive sequence speculation plateaus at a modest average acceptance rate of only 1.5 to 2.2 tokens per verification step, severely capping end-to-end serving acceleration.

2. The Single-Candidate Blindspot

Human language and reasoning are inherently branching trees, not rigid straight lines. In code generation, JSON parsing, or technical translation, multiple plausible token completions frequently exist at each decision boundary:

  • After "SELECT ", plausible candidates include ["id", "name", "*", "COUNT"].
  • After "if ", plausible completions include ["(", "x", "not", "len"].

A linear speculator is forced to commit to a single deterministic path. If the small draft model selects "*" but the target model prefers "id", the entire downstream speculation collapses, incurring wasted drafting overhead.

Inside SpecInfer: Tree-Based Speculative Inference and Verification

To overcome the brittle limitations of linear speculation, researchers from Carnegie Mellon University and partner institutions introduced SpecInfer: Accelerating Generative Large Language Model Serving with Tree-based Speculative Inference and Verification (ASPLOS 2024).

Rather than forcing the speculator to predict a single linear sequence, SpecInfer reorganizes speculative candidates into a Token Tree. By exploiting prediction diversity within an individual SSM or aggregating proposals across an ensemble of multiple diverse SSMs, SpecInfer maximizes the likelihood that the target model’s true output path is contained within the speculative candidate pool.

SpecInfer Architectural Flow: [Current Verified Context: S] │ ┌───────────────┴───────────────┐ ▼ ▼ [SSM Speculator: Expansion] [SSM Ensemble: Merge] │ │ └───────────────┬───────────────┘ │ ▼ [Constructed Token Tree N] │ ┌───────────────┴───────────────┐ │ Topology-Aware Causal Mask │ │ Depth-First KV Cache Fusion │ └───────────────┬───────────────┘ │ ▼ [Tree-Based Parallel Decoding: Target LLM (1 Pass)] │ ▼ [Token Tree Verifier] (Greedy / Stochastic Sampling) │ ┌───────────────┴───────────────┐ ▼ ▼ [Accepted Winning Path] [Dynamic KV Rollback] (e.g., 4 Verified Tokens) (Discard Unmatched Nodes)

1. Mathematical Formulation of the Token Tree

A token tree $\mathcal{N}$ is a directed acyclic tree structure where each node $u \in \mathcal{N}$ represents a token $t_u$, and $p_u$ designates $u$‘s parent node in the tree.

For each node $u$, $S_u$ represents the sequence of tokens formed by concatenating the ancestral path from the root node $r$ down to $u$:

$$S_u = S_{p_u} \circ {t_u}$$

Each path from the root node to a leaf node represents a distinct speculative completion hypothesis. Because overlapping candidate sequences share prefix nodes, the tree representation deduplicates identical prefixes, drastically reducing redundant drafting and verification computations.

2. Token Tree Construction: Expansion vs. Merge

SpecInfer provides two complementary mechanisms to construct high-coverage token trees:

Method A: Expansion-Based Tree Construction (Within a Single SSM) When serving a single SSM, SpecInfer expands candidate branches by sampling the top-$k$ tokens at each generation step. However, unconstrained branching leads to an exponential explosion in tree size ($k^m$).

SpecInfer adopts a configured expansion schedule $\langle k_1, k_2, \dots, k_m \rangle$, where $m$ is the maximum speculation depth and $k_i$ specifies the expansion factor at depth $i$. For example, an expansion configuration of $\langle 3, 2, 1, 1 \rangle$:

  • Level 1 expands the top-3 most probable tokens.
  • Level 2 expands the top-2 tokens for each of the 3 branches (yielding 6 nodes).
  • Levels 3 and 4 maintain linear extensions ($k=1$), bounding total tree size to a compact, hardware-friendly budget.

Method B: Merge-Based Tree Construction (Ensemble of Multiple SSMs) When multiple diverse SSMs are available (e.g., models fine-tuned on distinct data slices or specialized domains), SpecInfer generates candidate sequences from each model in parallel. It then merges the sequences into a unified token tree $\mathcal{M}$:

$$\forall u \in \mathcal{N}_i, \quad \exists v \in \mathcal{M} \quad \text{such that} \quad S_v = S_u$$

Merging identical prefixes ensures that the target LLM evaluates shared tokens exactly once, pooling the collective predictive capabilities of multiple draft models with zero verification redundancy.

Tree-Based Parallel Decoding & Topology-Aware Causal Masking

The central systems challenge in tree-based speculation is: How can a standard transformer model verify an entire branching tree of tokens in a single forward pass without launching multiple independent kernels or causing memory conflicts in the Key-Value cache?

SpecInfer solves this through two foundational innovations: Tree Attention and the Topology-Aware Causal Mask.

1. Generalizing Attention to Tree Topologies

In conventional transformer self-attention, token $i$ attends to all preceding tokens $j \le i$.

In tree attention, a candidate token $u \in \mathcal{N}$ must attend only to its ancestral tokens in the token tree, while ignoring tokens belonging to sibling or cousin branches:

$$\text{Attention}(Q_u, K, V) = \sum_{v \in \text{Ancestors}(u) \cup {u}} \alpha_{u, v} V_v$$

Where:

$$\alpha_{u, v} = \frac{\exp\left(\frac{Q_u K_v^T}{\sqrt{d_k}} + M_{u, v}\right)}{\sum_{w} \exp\left(\frac{Q_u K_w^T}{\sqrt{d_k}} + M_{u, w}\right)}$$

2. The Topology-Aware 2D Causal Mask

To fuse the attention computation of all tree nodes into a single GPU kernel, SpecInfer constructs a custom 2D causal mask matrix $M$:

$$M_{u, v} = \begin{cases} 0, & \text{if } v \in \text{Ancestors}(u) \cup {u} \ -\infty, & \text{otherwise} \end{cases}$$

Topology-Aware Causal Mask Example: Token Tree Structure: [t0: Root] ┌────┴────┐ [t1: "is"] [t2: "was"] ┌──┴──┐ │ [t3: "a"] [t4: "the"] [t5: "a"] Flattened Tree Order: [t0, t1, t2, t3, t4, t5] 2D Attention Mask M (0 = Attend, -inf = Masked): t0 t1 t2 t3 t4 t5 t0 [ 0 -inf -inf -inf -inf -inf ] t1 [ 0 0 -inf -inf -inf -inf ] (t1 attends to t0, t1) t2 [ 0 -inf 0 -inf -inf -inf ] (t2 attends to t0, t2; CANNOT see t1!) t3 [ 0 0 -inf 0 -inf -inf ] (t3 attends to t0, t1, t3) t4 [ 0 0 -inf -inf 0 -inf ] (t4 attends to t0, t1, t4; CANNOT see t3!) t5 [ 0 -inf 0 -inf -inf 0 ] (t5 attends to t0, t2, t5; CANNOT see t1, t3, t4!)

By passing this custom 2D boolean mask to an optimized attention kernel (such as FlashAttention or FlashDecoding), SpecInfer processes all candidate branches in parallel in a single matrix multiplication pass.

3. Depth-First Flattening and KV Cache Reuse

Storing Key-Value vectors for branching candidate tokens naively could lead to cache fragmentation. SpecInfer flattens tree nodes into memory using a Depth-First Search (DFS) order.

During the verification pass:

  1. All tree tokens append their computed Keys and Values to the active KV cache buffer.
  2. Once the target LLM completes its verification and selects the winning trajectory, non-selected branch tokens are immediately rolled back by adjusting the sequence length pointer.
  3. Only the verified path is permanently retained in the long-term KV cache, requiring zero memory reallocation or memory copying.

Exact Output Verification: Greedy and Stochastic Sampling

A mandatory requirement of speculative decoding is lossless mathematical fidelity: the output distribution of the accelerated serving system must be strictly identical to that of the original target LLM.

SpecInfer provides formal verification algorithms for both greedy and stochastic temperature sampling:

1. Greedy Verification Algorithm

For deterministic greedy decoding (temperature = 0), verification traverses the token tree from the root downwards:

  1. Start at the root node $r$.
  2. Let $t_{\text{pred}} = \text{argmax}{w} P{\text{target}}(w \mid S_r)$.
  3. Search among $r$‘s child nodes for a child $c$ where $t_c == t_{\text{pred}}$.
  4. If a matching child $c$ exists, accept $t_c$, advance to $c$, and repeat step 2.
  5. If no child matches, accept $t_{\text{pred}}$ as the final token for this iteration and terminate verification.

Because tree expansion explores multiple top candidate tokens at each branch, the probability of finding a continuous matching path of 3, 4, or 5 tokens is vastly higher than in linear single-sequence speculation.

2. Stochastic Verification Algorithm

For creative generation with temperature sampling ($T > 0$), SpecInfer implements a generalized tree rejection sampling algorithm that guarantees unbiased sampling from the target distribution $P(x)$:

  • At each tree node $u$, let $q(u)$ be the probability assigned by the speculator and $p(u)$ be the probability assigned by the target model.
  • If $p(u) \ge q(u)$, the candidate node is accepted with probability 1.
  • If $p(u) < q(u)$, the node is accepted with probability $\frac{p(u)}{q(u)}$.
  • If a node is rejected, SpecInfer draws a correction token from the normalized residual distribution:

$$P_{\text{residual}}(x) = \frac{\max(0, p(x) - q(x))}{\sum_w \max(0, p(w) - q(w))}$$

This guarantees zero output drift: end users receive the exact mathematical probability distribution of the base model while benefiting from accelerated inference throughput.

End-to-End Implementation: Building a Tree-Based Speculative Engine in PyTorch

In this section, we construct a fully functional Python implementation of SpecInfer’s core primitives: constructing a token tree, building a topology-aware 2D causal mask, and verifying candidate branches against target model logits.

import torch import torch.nn.functional as F from typing import List, Dict, Tuple, Optional class TokenTreeNode: def __init__(self, token_id: int, parent_idx: Optional[int] = None, depth: int = 0): self.token_id = token_id self.parent_idx = parent_idx self.depth = depth self.children_indices: List[int] = [] class SpecInferTreeManager: def __init__(self, expansion_schedule: List[int] = [3, 2, 1]): self.expansion_schedule = expansion_schedule def construct_expansion_tree( self, draft_model, input_ids: torch.Tensor, tokenizer ) -> Tuple[List[TokenTreeNode], torch.Tensor, torch.Tensor]: device = input_ids.device nodes: List[TokenTreeNode] = [] current_level_indices = [] with torch.no_grad(): outputs = draft_model(input_ids) next_token_logits = outputs.logits[:, -1, :] topk_probs, topk_tokens = torch.topk(next_token_logits, self.expansion_schedule[0]) for tok in topk_tokens[0]: node_idx = len(nodes) node = TokenTreeNode(token_id=tok.item(), parent_idx=None, depth=0) nodes.append(node) current_level_indices.append(node_idx) for depth_step, k_branch in enumerate(self.expansion_schedule[1:], start=1): next_level_indices = [] for parent_idx in current_level_indices: ancestral_tokens = self._get_ancestral_tokens(nodes, parent_idx) branch_input = torch.cat([input_ids[0], torch.tensor(ancestral_tokens, device=device)]).unsqueeze(0) with torch.no_grad(): branch_out = draft_model(branch_input) branch_logits = branch_out.logits[:, -1, :] _, branch_tokens = torch.topk(branch_logits, k_branch) for child_tok in branch_tokens[0]: child_idx = len(nodes) child_node = TokenTreeNode(token_id=child_tok.item(), parent_idx=parent_idx, depth=depth_step) nodes[parent_idx].children_indices.append(child_idx) nodes.append(child_node) next_level_indices.append(child_idx) current_level_indices = next_level_indices flat_tokens = torch.tensor([n.token_id for n in nodes], dtype=torch.long, device=device) mask = self._generate_topology_mask(nodes, device) return nodes, flat_tokens, mask def _get_ancestral_tokens(self, nodes: List[TokenTreeNode], node_idx: int) -> List[int]: path = [] curr = node_idx while curr is not None: path.append(nodes[curr].token_id) curr = nodes[curr].parent_idx path.reverse() return path def _generate_topology_mask(self, nodes: List[TokenTreeNode], device: torch.device) -> torch.Tensor: N = len(nodes) mask = torch.full((N, N), float('-inf'), device=device) for u in range(N): mask[u, u] = 0.0 curr = nodes[u].parent_idx while curr is not None: mask[u, curr] = 0.0 curr = nodes[curr].parent_idx return mask def verify_greedy( self, nodes: List[TokenTreeNode], target_logits: torch.Tensor ) -> Tuple[List[int], int]: accepted_tokens: List[int] = [] candidates = [i for i, n in enumerate(nodes) if n.parent_idx is None] curr_active_node = None while candidates: matched_candidate = None for cand_idx in candidates: cand_node = nodes[cand_idx] if cand_node.parent_idx is None: matched_candidate = cand_idx break else: parent_pred = torch.argmax(target_logits[cand_node.parent_idx]).item() if cand_node.token_id == parent_pred: matched_candidate = cand_idx break if matched_candidate is not None: accepted_tokens.append(nodes[matched_candidate].token_id) curr_active_node = matched_candidate candidates = nodes[matched_candidate].children_indices else: if curr_active_node is not None: correction_tok = torch.argmax(target_logits[curr_active_node]).item() accepted_tokens.append(correction_tok) break return accepted_tokens, len(accepted_tokens)

Complete Verification Test Harness

Let’s verify how the tree manager constructs a topology-aware mask and opportunistically verifies tokens:

if __name__ == "__main__": device = torch.device("cpu") tree_manager = SpecInferTreeManager(expansion_schedule=[2, 2]) mock_nodes = [ TokenTreeNode(token_id=101, parent_idx=None, depth=0), # 0 TokenTreeNode(token_id=102, parent_idx=None, depth=0), # 1 TokenTreeNode(token_id=201, parent_idx=0, depth=1), # 2 (child of 0) TokenTreeNode(token_id=202, parent_idx=0, depth=1), # 3 (child of 0) TokenTreeNode(token_id=301, parent_idx=1, depth=1), # 4 (child of 1) TokenTreeNode(token_id=302, parent_idx=1, depth=1), # 5 (child of 1) ] mock_nodes[0].children_indices = [2, 3] mock_nodes[1].children_indices = [4, 5] mask = tree_manager._generate_topology_mask(mock_nodes, device) print("Topology-Aware Causal Mask (0.0 = Attend, -inf = Masked):") print((mask == 0.0).int()) vocab_size = 500 target_logits = torch.randn(len(mock_nodes), vocab_size) target_logits[0, 201] = 100.0 # Node 0 predicts token 201 (Node 2) target_logits[2, 450] = 100.0 # Node 2 predicts token 450 (Leaf correction) accepted, count = tree_manager.verify_greedy(mock_nodes, target_logits) print(f"\nAccepted Tokens: {accepted} (Total Decoded: {count})")

Empirical Evaluation: Distributed Serving & Offloading Benchmarks

In extensive evaluations presented at ASPLOS 2024 across diverse open-source model pairs (LLaMA-7B/13B/33B/65B and OPT models) on NVIDIA A100 and RTX 3090 GPUs, SpecInfer demonstrated decisive speedups over both standard auto-regressive decoding and linear speculative baselines.

1. Distributed Serving Throughput (A100 GPUs)

When serving large foundation models distributed across NVLink-interconnected GPUs:

Target ModelSpeculator (SSM)DatasetIncremental BaselineLinear SpeculationSpecInfer (Tree)Net Speedup
LLaMA-33BLLaMA-7BAlpaca (General QA)18.2 ms/tok11.4 ms/tok6.5 ms/tok2.80x
LLaMA-33BLLaMA-7BGSM8K (Math Reasoning)19.1 ms/tok13.8 ms/tok7.1 ms/tok2.69x
LLaMA-65BLLaMA-7BHumanEval (Code)34.5 ms/tok22.1 ms/tok12.7 ms/tok2.72x
OPT-66BOPT-13BWikiText (Language)36.2 ms/tok24.5 ms/tok13.9 ms/tok2.60x

Across all workloads, SpecInfer achieved an average speedup of 2.6x to 2.8x, outperforming linear speculative decoding by 35% to 55% due to higher tree acceptance lengths.

2. Offloading-Based Serving (Single GPU + Host CPU RAM)

In resource-constrained workstation environments where large model weights must be streamed over PCIe from host RAM to GPU VRAM (such as running LLaMA-65B on an RTX 3090):

Workload ConfigurationIncremental DecodingLinear Speculative SamplingSpecInfer (Tree Attention)Offloading Speedup
LLaMA-65B on 24GB GPU (PCIe Gen4)285 ms/token142 ms/token81.4 ms/token3.50x
LLaMA-33B on 16GB GPU (PCIe Gen4)148 ms/token79 ms/token43.5 ms/token3.40x

In offloading configurations, the PCIe transfer of target model weights dominates 90%+ of total latency. By accepting 3 to 4 tokens per PCIe weight swap instead of 1, SpecInfer slashed total memory traffic by over 70%, yielding 3.5x end-to-end acceleration.

Architectural Comparison: SpecInfer vs. Modern Speculative Frameworks

To evaluate where SpecInfer sits within modern inference optimization stacks, review this architectural comparison:

DimensionClassical Speculative DecodingMedusa (Multi-Head)Sequoia (Dynamic Tree)SpecInfer (ASPLOS ‘24)
Speculation TopologyLinear Chain ($K$ tokens)Static Tree HeadsDynamic Programming TreeFlexible Tree (Expansion & Merge)
Draft ArchitectureSingle Small Model (SSM)Multiple Linear HeadsSingle Small Model (SSM)Single SSM or Multi-SSM Ensemble
Target VerificationBatched SequenceCustom Tree MaskHardware-Calibrated Tree MaskTopology-Aware Causal Mask
Training RequirementPre-trained SSMSupervised Fine-TuningPre-trained SSMOff-the-shelf SSM or Fine-tuned Ensemble
Output Distribution100% Exact MatchHeuristic / Truncated100% Exact Match100% Exact Match (Lossless)
Offloading SupportStandardLimitedSupportedNative Optimized Offloading (3.5x)

Unlike Medusa (which requires modifying target model architectures and training auxiliary heads), SpecInfer works with off-the-shelf, frozen target LLMs. Furthermore, SpecInfer pioneered the fundamental 2D topology-aware causal masking technique that subsequent tree-based systems (like Sequoia and EAGLE-2) adopted and refined.

For engineering teams operating scalable data collection infrastructure, document transformation platforms, or AI research agents via AiFetchly API integration, SpecInfer’s tree-based parallel verification provides the blueprint for maximizing inference throughput under strict Service Level Objectives (SLOs).

Production Engineering: Deployment Best Practices & Serving Guidelines

Deploying SpecInfer in high-throughput enterprise production requires calibrating tree depth, managing memory budgets, and tuning batching schedulers:

Production Serving Topology: ┌────────────────────────────────────────────────────────┐ │ Client Gateway: gRPC / HTTP Streaming Request │ └───────────────────────────┬────────────────────────────┘ │ Continuous Batching Dispatch ▼ ┌────────────────────────────────────────────────────────┐ │ SpecInfer Inference Engine: │ │ ├── 1. Dynamic Tree Schedule Calibration │ │ ├── 2. Topology-Aware Causal Attention Kernel │ │ ├── 3. PagedKV Block Pool with Depth-First Rollback │ │ └── 4. Asynchronous SSM Prefetching │ └────────────────────────────────────────────────────────┘

1. Dynamic Tree Budget Calibration

Tree sizes should adapt to current GPU execution regimes:

  • Low Concurrency (Batch Size 1–4): The GPU is severely memory-bandwidth bound. Expand larger trees (e.g., 32–64 candidate tokens) to maximize parallel token verification per weight read.
  • High Concurrency (Batch Size 32+): The GPU transitions into a compute-bound state. Reduce tree size (e.g., 8–16 tokens) to prevent tree attention computation from exceeding the latency savings of speculation.

2. Integration with Continuous Batching

In high-throughput multi-tenant clusters, requests have variable prompt lengths and generation states:

  • Maintain independent token tree buffers per request within the continuous batching scheduler.
  • Pad topology-aware masks using ragged or nested tensor formats to eliminate redundant FLOPs on unallocated matrix cells.

3. PagedKV Allocation with Zero-Copy Pruning

When verifying tree candidates, allocate temporary KV cache blocks using a unified PagedAttention memory manager:

  • Mark candidate tree blocks as speculative.
  • Upon verification completion, release unselected branch pages back to the free memory pool in a single metadata update, avoiding memory fragmentation.

To explore how advanced serving architectures can accelerate your enterprise data workflows, review the AiFetchly documentation and explore our flexible pricing options for automated data extraction pipelines.

Frequently Asked Questions (FAQ)

Does SpecInfer alter the output quality or accuracy of the target LLM?

No. SpecInfer uses exact rejection sampling algorithms (both for deterministic greedy decoding and temperature-based stochastic sampling) that are mathematically proven to draw completions strictly from the target model’s probability distribution. The accelerated output matches standard autoregressive decoding token-for-token.

How does SpecInfer differ from standard speculative decoding?

Standard speculative decoding evaluates a single linear sequence of candidate tokens; if any token is rejected, all subsequent drafted tokens are wasted. SpecInfer organizes candidates into a branching token tree and verifies all paths in parallel using a custom topology-aware causal mask, drastically increasing the likelihood of accepting multiple tokens per step.

Can SpecInfer be used with off-the-shelf open-source models?

Yes. SpecInfer requires no structural modifications or retraining of the target foundation model. Any standard transformer model (such as LLaMA-3, Qwen-2.5, or Mistral) can be paired with compatible small speculative models directly.

What hardware delivers the greatest speedup with SpecInfer?

SpecInfer delivers substantial benefits in memory-bandwidth-bound environments: single-GPU serving with low batch sizes (2.0x–2.8x speedup) and offloading-based serving where model weights stream over PCIe from CPU RAM to GPU VRAM (up to 3.5x speedup).