<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Tensor-Parallelism on lo0 — Blog Técnico</title><link>https://blog.lo0.es/tags/tensor-parallelism/</link><description>Recent content in Tensor-Parallelism on lo0 — Blog Técnico</description><generator>Hugo -- gohugo.io</generator><language>es</language><lastBuildDate>Fri, 05 Jun 2026 04:00:00 +0000</lastBuildDate><atom:link href="https://blog.lo0.es/tags/tensor-parallelism/index.xml" rel="self" type="application/rss+xml"/><item><title>Una réplica grande o muchas pequeñas: la decisión que define tu plataforma</title><link>https://blog.lo0.es/posts/tp-replicas-una-grande-vs-n-pequenas/</link><pubDate>Fri, 05 Jun 2026 04:00:00 +0000</pubDate><guid>https://blog.lo0.es/posts/tp-replicas-una-grande-vs-n-pequenas/</guid><description>&lt;h2 id="tldr">TL;DR&lt;/h2>
&lt;p>Con 4 GPUs disponibles tienes dos opciones básicas: una instancia de vLLM usando las 4 (TP=4) o dos instancias independientes usando 2 cada una (TP=2 × 2 réplicas). La primera da menor latencia por request individual. La segunda da mayor throughput agregado a alta concurrencia, mejor fault tolerance y escala más fino. El punto de cruce —cuando la segunda supera a la primera— está típicamente entre 16 y 64 requests concurrentes para modelos 70B, mucho antes de lo que la mayoría asume. La métrica que lo decide: goodput, los tokens generados dentro del SLO de latencia dividido por el total.&lt;/p>
&lt;hr>
&lt;h2 id="la-analogía">La analogía&lt;/h2>
&lt;p>Dos formas de organizar un servicio de traducción: un traductor senior con acceso a cuatro diccionarios especializados simultáneamente (puede resolver cualquier consulta compleja en 30 segundos), o dos traductores junior cada uno con dos diccionarios (tardan 45 segundos por consulta compleja, pero pueden atender dos simultáneamente).&lt;/p>
&lt;p>Para un cliente que llega solo y espera respuesta rápida: el senior gana. Para una cola de veinte clientes llegando a la vez: los dos juniors procesan el doble de consultas por hora aunque cada una tarde más. La pregunta no es quién es mejor, sino qué tipo de tráfico tienes.&lt;/p>
&lt;hr>
&lt;h2 id="las-dos-arquitecturas-en-vllm">Las dos arquitecturas en vLLM&lt;/h2>
&lt;h3 id="arquitectura-a-tp4-una-réplica">Arquitectura A: TP=4, una réplica&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Una sola instancia usa las 4 GPUs vía tensor parallelism&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">vllm serve meta-llama/Meta-Llama-3.1-70B-Instruct &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --tensor-parallel-size &lt;span class="m">4&lt;/span> &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --gpu-memory-utilization 0.92 &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --port &lt;span class="m">8000&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;pre tabindex="0">&lt;code>GPU-0 ─┐
GPU-1 ─┤─ vLLM instance 0 ──► puerto 8000
GPU-2 ─┤ (TP=4, el modelo se
GPU-3 ─┘ reparte entre 4 GPUs)
&lt;/code>&lt;/pre>&lt;p>Cada operación de atención y FFN se divide entre 4 GPUs. Requieren comunicación all-reduce después de cada capa (en NVLink: ~50-200 µs; en PCIe: ~2-8 ms). El modelo completo está disponible en la VRAM agregada.&lt;/p>
&lt;h3 id="arquitectura-b-tp2--2-réplicas">Arquitectura B: TP=2 × 2 réplicas&lt;/h3>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-bash" data-lang="bash">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Dos instancias independientes, cada una con 2 GPUs&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Instancia 0 en GPU 0-1&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">CUDA_VISIBLE_DEVICES&lt;/span>&lt;span class="o">=&lt;/span>0,1 vllm serve meta-llama/Meta-Llama-3.1-70B-Instruct &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --tensor-parallel-size &lt;span class="m">2&lt;/span> --port &lt;span class="m">8000&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Instancia 1 en GPU 2-3&lt;/span>
&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="nv">CUDA_VISIBLE_DEVICES&lt;/span>&lt;span class="o">=&lt;/span>2,3 vllm serve meta-llama/Meta-Llama-3.1-70B-Instruct &lt;span class="se">\
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="se">&lt;/span> --tensor-parallel-size &lt;span class="m">2&lt;/span> --port &lt;span class="m">8001&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;pre tabindex="0">&lt;code>GPU-0 ─┐ ┌─► puerto 8000
GPU-1 ─┘─ vLLM instance 0 ─┘
← load balancer
GPU-2 ─┐ ┌─► puerto 8001
GPU-3 ─┘─ vLLM instance 1 ─┘
&lt;/code>&lt;/pre>&lt;p>Cada instancia tiene la mitad del modelo. Las requests se distribuyen entre instancias. Sin comunicación entre instancias (son completamente independientes).&lt;/p>
&lt;hr>
&lt;h2 id="por-qué-tp4-tiene-mayor-latencia-individual-que-tp2">Por qué TP=4 tiene mayor latencia individual que TP=2&lt;/h2>
&lt;p>El tensor parallelism divide cada capa del transformer. Después de calcular su fracción, cada GPU necesita sincronizarse con las otras vía all-reduce antes de pasar a la siguiente capa. El coste de esta sincronización:&lt;/p>
&lt;p>$$\text{overhead_TP} = n_layers \times 2 \times \text{latencia_allreduce}$$&lt;/p>
&lt;p>Para Llama 3 70B (80 capas) en 4×H100 NVLink:&lt;/p>
&lt;p>$$\text{overhead_TP4} = 80 \times 2 \times 100,\mu s = 16,ms$$&lt;/p>
&lt;p>En PCIe (sin NVLink directo entre GPUs):&lt;/p>
&lt;p>$$\text{overhead_TP4_PCIe} = 80 \times 2 \times 3,ms = 480,ms$$&lt;/p>
&lt;p>Ese overhead se suma a cada paso de decode. Con TP=2:&lt;/p>
&lt;p>$$\text{overhead_TP2} = 80 \times 2 \times 60,\mu s = 9.6,ms \text{ (NVLink)}$$&lt;/p>
&lt;p>La diferencia entre TP=2 y TP=4 en NVLink es ~6 ms por paso de decode —relevante para TPOT (inter-token latency) en aplicaciones de streaming.&lt;/p>
&lt;p>En PCIe sin NVLink directo: TP=4 puede ser &lt;strong>400 ms más lento por paso&lt;/strong> que TP=2. Para un output de 200 tokens, eso son 80 segundos adicionales. En este escenario, TP=4 PCIe nunca debe usarse salvo que el modelo no quepa en 2 GPUs.&lt;/p>
&lt;hr>
&lt;h2 id="el-punto-de-cruce-cuándo-tp22-supera-a-tp41">El punto de cruce: cuándo TP=2×2 supera a TP=4×1&lt;/h2>
&lt;p>Para un modelo 70B en 4×H100 SXM (NVLink), el throughput agregado en tokens/segundo:&lt;/p>
&lt;pre tabindex="0">&lt;code>Concurrencia | TP=4 × 1 instancia | TP=2 × 2 instancias | Ganador
──────────────┼──────────────────────┼───────────────────────┼────────
1 | 200 tok/s | 170 tok/s | TP=4 (latencia)
4 | 650 tok/s | 620 tok/s | TP=4 (ligero)
16 | 1.800 tok/s | 2.100 tok/s | TP=2×2
32 | 2.400 tok/s | 3.600 tok/s | TP=2×2 (+50%)
64 | 2.800 tok/s | 5.200 tok/s | TP=2×2 (+86%)
128 | 2.900 tok/s | 5.800 tok/s | TP=2×2 (+100%)
&lt;/code>&lt;/pre>&lt;p>Por qué divergen a alta concurrencia: con TP=4, el scheduler de una sola instancia gestiona todas las requests pero el KV cache es compartido. Con TP=2×2, cada instancia tiene su propio scheduler y KV cache: menos contención, más paralelismo real.&lt;/p>
&lt;p>El punto de cruce en NVLink está alrededor de &lt;strong>16-32 requests simultáneos&lt;/strong> para 70B. Para modelos más pequeños (14B, 7B), el cruce ocurre antes porque el overhead de comunicación TP pesa más relativamente.&lt;/p>
&lt;hr>
&lt;h2 id="las-tres-implicaciones-que-nadie-menciona">Las tres implicaciones que nadie menciona&lt;/h2>
&lt;h3 id="1-fault-tolerance">1. Fault tolerance&lt;/h3>
&lt;p>Con TP=4 × 1 réplica: si una GPU falla, la instancia entera cae. El servicio baja al 0% hasta que la GPU se recupera o el pod se reinicia en otro nodo.&lt;/p>
&lt;p>Con TP=2 × 2 réplicas: si una GPU falla, cae una instancia. El servicio sigue al 50% de capacidad. Para ENS/NIS2 donde la disponibilidad es un requisito contractual, esta diferencia es determinante.&lt;/p>
&lt;h3 id="2-granularidad-de-autoscaling">2. Granularidad de autoscaling&lt;/h3>
&lt;p>Con KEDA o HPA basado en &lt;code>vllm:num_waiting_seqs&lt;/code>, el autoscaling debe provisionar en múltiplos de la unidad de deploy:&lt;/p>
&lt;ul>
&lt;li>TP=4 × 1: cada nuevo nodo requiere 4 GPUs. La granularidad mínima de escala es 4 GPUs.&lt;/li>
&lt;li>TP=2 × 2: cada nuevo pod requiere 2 GPUs. La granularidad mínima es 2 GPUs — más fino, más eficiente en coste.&lt;/li>
&lt;/ul>
&lt;h3 id="3-degradación-de-calidad-bajo-carga">3. Degradación de calidad bajo carga&lt;/h3>
&lt;p>TP=4 con muchos requests concurrentes empieza a tener preemptions cuando el KV cache se llena. TP=2×2 distribuye esa presión entre dos pools independientes de KV cache — la probabilidad de preemption es menor bajo la misma carga total.&lt;/p>
&lt;hr>
&lt;h2 id="medir-el-punto-de-cruce-con-otel">Medir el punto de cruce con OTel&lt;/h2>
&lt;p>El goodput es la métrica correcta para comparar las dos arquitecturas. No el throughput bruto (que ignora el SLO), sino los tokens que se generan dentro del SLO de TPOT acordado:&lt;/p>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-promql" data-lang="promql">&lt;span class="line">&lt;span class="cl">&lt;span class="c1"># Goodput: tokens generados con TPOT dentro del SLO (ej: &amp;lt;50ms/token)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1"># Para TP=4:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="kr">rate&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">vllm&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="nv">generation_tokens_total&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nl">instance&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">&amp;#34;&lt;/span>&lt;span class="s">tp4&lt;/span>&lt;span class="p">&amp;#34;}[&lt;/span>&lt;span class="s">5m&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kr">histogram_quantile&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="mf">0.95&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kr">rate&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">vllm&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="nv">time_per_output_token_seconds_bucket&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nl">instance&lt;/span>&lt;span class="o">=&lt;/span>&lt;span class="p">&amp;#34;&lt;/span>&lt;span class="s">tp4&lt;/span>&lt;span class="p">&amp;#34;}[&lt;/span>&lt;span class="s">5m&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mf">0.050&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c1"># Para TP=2×2 (suma de las dos instancias):&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="k">sum&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="kr">rate&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">vllm&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="nv">generation_tokens_total&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nl">instance&lt;/span>&lt;span class="o">=~&lt;/span>&lt;span class="p">&amp;#34;&lt;/span>&lt;span class="s">tp2-.*&lt;/span>&lt;span class="p">&amp;#34;}[&lt;/span>&lt;span class="s">5m&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">))&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="o">*&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="mi">1&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">-&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="kr">histogram_quantile&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="mf">0.95&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="k">sum&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="kr">rate&lt;/span>&lt;span class="o">(&lt;/span>&lt;span class="nv">vllm&lt;/span>&lt;span class="err">:&lt;/span>&lt;span class="nv">time_per_output_token_seconds_bucket&lt;/span>&lt;span class="p">{&lt;/span>&lt;span class="nl">instance&lt;/span>&lt;span class="o">=~&lt;/span>&lt;span class="p">&amp;#34;&lt;/span>&lt;span class="s">tp2-.*&lt;/span>&lt;span class="p">&amp;#34;}[&lt;/span>&lt;span class="s">5m&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="o">)))&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="o">&amp;gt;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="mf">0.050&lt;/span>&lt;span class="o">)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>La comparación directa en el mismo dashboard, con tráfico sintético a distintos niveles de concurrencia, determina el punto de cruce exacto para tu hardware y modelo.&lt;/p>
&lt;hr>
&lt;h2 id="la-decisión-por-perfil-de-workload">La decisión por perfil de workload&lt;/h2>
&lt;table>
&lt;thead>
&lt;tr>
&lt;th style="text-align:left">Perfil&lt;/th>
&lt;th style="text-align:left">Arquitectura recomendada&lt;/th>
&lt;th style="text-align:left">Razón&lt;/th>
&lt;/tr>
&lt;/thead>
&lt;tbody>
&lt;tr>
&lt;td style="text-align:left">Chatbot usuario único / baja concurrencia (&amp;lt;10 simultáneos)&lt;/td>
&lt;td style="text-align:left">TP=4 × 1&lt;/td>
&lt;td style="text-align:left">Latencia p50 más baja, experiencia de streaming mejor&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">API enterprise (20-100 concurrentes)&lt;/td>
&lt;td style="text-align:left">TP=2 × 2&lt;/td>
&lt;td style="text-align:left">Goodput superior, fault tolerance, autoscaling más fino&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">Batch processing (throughput &amp;gt; latencia)&lt;/td>
&lt;td style="text-align:left">TP=2 × 2 (o más réplicas)&lt;/td>
&lt;td style="text-align:left">Throughput máximo siempre en réplicas&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">Modelo muy grande (&amp;gt;80B, no cabe en 2 GPUs)&lt;/td>
&lt;td style="text-align:left">TP=4 × 1&lt;/td>
&lt;td style="text-align:left">Sin alternativa estructural&lt;/td>
&lt;/tr>
&lt;tr>
&lt;td style="text-align:left">ENS/disponibilidad contractual&lt;/td>
&lt;td style="text-align:left">TP=2 × 2 mínimo&lt;/td>
&lt;td style="text-align:left">La caída de una GPU no es catastrófica&lt;/td>
&lt;/tr>
&lt;/tbody>
&lt;/table>
&lt;hr>
&lt;h2 id="configuración-en-kubernetes-con-ambas-arquitecturas">Configuración en Kubernetes con ambas arquitecturas&lt;/h2>
&lt;div class="highlight">&lt;pre tabindex="0" class="chroma">&lt;code class="language-yaml" data-lang="yaml">&lt;span class="line">&lt;span class="cl">&lt;span class="c"># Deployments paralelos para A/B test o topologías distintas&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># Instancias TP=2 (2 réplicas por deployment)&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">apps/v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Deployment&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">vllm-tp2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">replicas&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">template&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">containers&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">vllm&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">args&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="p">[&lt;/span>&lt;span class="s2">&amp;#34;serve&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;meta-llama/Meta-Llama-3.1-70B-Instruct&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;--tensor-parallel-size&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;2&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;--gpu-memory-utilization&amp;#34;&lt;/span>&lt;span class="p">,&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;0.92&amp;#34;&lt;/span>&lt;span class="p">]&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">resources&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">limits&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">nvidia.com/gpu&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="s2">&amp;#34;2&amp;#34;&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 2 GPUs por pod&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nn">---&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="c"># Service con load balancing entre las 2 réplicas&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">apiVersion&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">v1&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">kind&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">Service&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">metadata&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">name&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">vllm-tp2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w">&lt;/span>&lt;span class="nt">spec&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">selector&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">app&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">vllm-tp2&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">ports&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>- &lt;span class="nt">port&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">8000&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">sessionAffinity&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="l">ClientIP &lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># para prefix cache awareness&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">sessionAffinityConfig&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">clientIP&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;span class="line">&lt;span class="cl">&lt;span class="w"> &lt;/span>&lt;span class="nt">timeoutSeconds&lt;/span>&lt;span class="p">:&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="m">10800&lt;/span>&lt;span class="w"> &lt;/span>&lt;span class="c"># 3 horas de afinidad por sesión&lt;/span>&lt;span class="w">
&lt;/span>&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/div>&lt;p>La &lt;code>sessionAffinity: ClientIP&lt;/code> en el Service de Kubernetes es la forma más sencilla de implementar routing con afinidad por sesión — las requests del mismo cliente van siempre a la misma réplica, maximizando el hit rate del prefix cache del historial de conversación.&lt;/p>
&lt;hr>
&lt;h2 id="ver-también">Ver también&lt;/h2>
&lt;ul>
&lt;li>https://blog.lo0.es/posts/batch-sizing-vllm-grid-search/ — el grid search de max-num-seqs cambia con la arquitectura: una réplica grande tolera max-num-seqs más alto que dos pequeñas con el mismo KV cache total&lt;/li>
&lt;li>https://blog.lo0.es/posts/prefix-cache-hit-rate-engineering/ — el routing por sesión (&lt;code>sessionAffinity&lt;/code>) es la implementación K8s del prefix-aware routing: mismo cliente, misma réplica, mismo cache&lt;/li>
&lt;li>https://blog.lo0.es/posts/autoscaling-llm-kubernetes-keda/ — KEDA escala en unidades de pod; TP=2×2 da granularidad de 2 GPUs vs 4 GPUs para TP=4×1, impactando el coste del autoscaling reactivo&lt;/li>
&lt;li>https://blog.lo0.es/posts/vllm-otel-instrumentacion-optimizaciones/ — goodput calculado sobre &lt;code>generation_tokens_total&lt;/code> y &lt;code>time_per_output_token_seconds&lt;/code> son las métricas que comparan las dos arquitecturas&lt;/li>
&lt;li>https://blog.lo0.es/posts/disaggregated-serving-prefill-decode/ — el siguiente nivel de separación cuando ni TP=4×1 ni TP=2×2 son suficientes: separar el hardware de prefill del de decode&lt;/li>
&lt;li>https://blog.lo0.es/posts/fp8-end-to-end-pesos-kv-calidad/ — FP8 libera VRAM en cada réplica; combinado con TP=2×2, el impacto se multiplica: más concurrencia por réplica y más réplicas posibles&lt;/li>
&lt;/ul>
&lt;hr>
&lt;h2 id="referencias">Referencias&lt;/h2>
&lt;ul>
&lt;li>&lt;a href="https://github.com/vllm-project/vllm/issues/16300">vLLM issue #16300: TP=8 peor que TP=4 en 8×A100&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://docs.jarvislabs.ai/blog/scaling-llm-inference-dp-pp-tp">Scaling LLM Inference: DP, PP &amp;amp; TP en vLLM — Jarvislabs&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://rocm.blogs.amd.com/software-tools-optimization/vllm-moe-guide/README.html">vLLM MoE Playbook: TP, DP, PP and Expert Parallelism — ROCm Blogs&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.databasemart.com/blog/vllm-distributed-inference-optimization-guide">vLLM Distributed Inference Optimization Guide — DatabaseMart&lt;/a>&lt;/li>
&lt;/ul></description></item></channel></rss>