Мазмұнға өту
6 мин оқу

LLM API-де p95 кідірісін өлшеу жолы

p95 кідірісін өлшеу жолын талдаймыз: k6, OpenTelemetry және trace арқылы желі, шлюз, GPU кезегі мен генерация уақытын бөлеміз.

LLM API-де p95 кідірісін өлшеу жолы

Бір баяу генерацияның кемінде төрт ықтимал иесі бар: желі, API шлюзі, GPU алдындағы кезек және модельдің өзі. Тек http_req_duration көрсеткішіне қарасаңыз, олардың бәрі бір p95 мәніне бірігеді де, команда болжамға сүйеніп дауласа бастайды. Желі инженерлері inference-ті, ML инженерлері проксиді кінәлайды, ал график дауды шеше алмайды.

Дұрыс өлшем бір сұрауға қатысты екі бақылауды біріктіреді. k6 уақытты сырттан тіркейді, ал OpenTelemetry спандары сервер ішіндегі жұмысты өлшейді. Оларды ортақ trace ID байланыстырады. Абсолют уақыт белгілерін емес, ұзақтықтарды азайту керек: жүктеме генераторы мен сервер сағаттары ондаған миллисекундты талдауға жететіндей дәлдікпен синхрондала бермейді.

Бір p95 төрт түрлі кідірісті жасырады

Сұрауды тесті бастамай тұрып бөліктерге ажыратыңыз, әйтпесе фазалардың атауын көріп қойған графикке қарай бейімдейсіз. Кәдімгі HTTP жауабы үшін клиент уақытын былай көрсетуге болады:

client_total = client_transport + gateway + gpu_queue + generation + response_transfer
server_total = gateway + gpu_queue + generation
network_envelope ≈ client_total - server_total

client_transport клиенттегі бос қосылымды күтуді, DNS, TCP, TLS және сұрау денесін жіберуді қамтиды. gateway аутентификацияны, rate limit тексеруін, деректерді бүркемелеуді, маршрут таңдауды, сұрауды түрлендіруді және провайдерге жүгінуді қамтиды. gpu_queue сұрау орындауға дайын болғанымен, есептеу слоты әлі берілмеген кезде басталады. generation inference нақты басталған сәттен соңғы токенге немесе тоқтатуға дейін созылады.

Формуланың соңғы жолын әдейі желілік конверт деп атаймыз. Айырмаға екі бағыттағы жол, сервердің түбірлік спанына кірмейтін балансерлер, жауапты тасымалдау және клиенттің шағын шығындары кіреді. Бұл таза RTT емес. Команда айырманы жай ғана «желі» деп атаса, бір күні кідірісті спан жабылғаннан кейінгі жауап компрессиясы қосып тұрса да, байланыс арнасын жөндеуге кіріседі.

Ағынды жауапқа екі өлшем қажет. Бірінші байтқа дейінгі уақыт, LLM үшін көбіне бірінші токенге дейінгі уақыт, жүйенің қаншалықты тез жауап бере бастағанын көрсетеді. Толық ұзақтық пайдаланушы бүкіл нәтижені қашан алғанын көрсетеді. Бір сұраудың TTFT көрсеткіші қалыпты болып, генерациясы ұзақ жүруі мүмкін немесе TTFT нашар болғанымен, кейінгі токендер жылдам келуі мүмкін. Бұл жағдайларды бір SLO ішінде араластыруға болмайды.

Қолданба кезегі мен GPU кезегін ажыратыңыз. HTTP қосылымдары пулында, шлюз семафорында немесе брокерде күту сұрауды ұстап тұрған компонентке жатады. GPU queue inference жоспарлаушысы жұмысты қабылдағаннан кейін ғана басталады. Бұл айырмашылық инцидент иесін және түзету тәсілін анықтайды: шлюз репликаларын көбейту модельдің есептеу слоттарын босатпайды.

Өтпелі trace k6 ішінде басталуға тиіс

Жүктеменің әр сұрауында бірегей W3C traceparent болуы керек. Сонда k6 жазбасын серверлік спандар арасынан уақыт пен URL бойынша іздемей-ақ табасыз. W3C Trace Context ұсынымы version-trace-id-parent-id-flags пішімін белгілейді; trace ID 32 оналтылық таңбадан, parent ID 16 таңбадан тұрады. Түгел нөлден тұратын идентификаторларға рұқсат жоқ.

Тестке толық tracing SDK қажет емес. Дұрыс тақырып жолын жасап, trace ID мәнін пайдаланушы метрикасының тегі немесе диагностикалық лог өрісі ретінде сақтау жеткілікті. Келесі фрагмент әр VU мен итерацияға жеке идентификатор ауқымын береді:

import exec from 'k6/execution';

function hex(value, width) {
  return Math.floor(value).toString(16).padStart(width, '0').slice(-width);
}

export function traceContext() {
  const vu = exec.vu.idInTest;
  const iteration = exec.scenario.iterationInTest + 1;
  const traceId = hex(vu, 16) + hex(iteration, 16);
  const parentId = hex(vu * 1000000 + iteration, 16);
  return {
    traceId,
    header: `00-${traceId}-${parentId}-01`,
  };
}

Мәндер JavaScript Number қауіпсіз ауқымында тұрғанда, мұндай генератор бақыланатын тестке жарайды. Бірнеше процесте таратылған іске қосу үшін жүктеме генераторы данасының идентификаторын trace ID жоғарғы бөлігіне қосыңыз немесе k6 кеңейтімінің криптографиялық генераторын қолданыңыз. Коллизия жоғалған trace-тен қауіпті, өйткені ол екі тәуелсіз оқиғаны біріктіріп, фаза квантильдерін бұзады.

Шлюз кіріс контекстін қабылдап, SERVER спанын жасап, жаңартылған контексті әрі қарай беруі керек. Аралық прокси traceparent тақырыбын алып тастаса, тізбек үзіледі. Мұны жүктемеге дейін бір сұраумен тексеріңіз: бір trace ішінде клиенттік parent, шлюз спаны, кезекті күту және генерация болуы керек. 01 жалауы trace жазуды сұрайды, бірақ W3C спецификациясы оның сақталуына кепілдік бермейді. Сондықтан сервердегі sampling саясаты да тест конфигурациясына кіреді.

Trace ID мәнін k6 стандартты метрикаларының бәріне тег ретінде қоспаңыз. Әр сұрауға бірегей тег беру кардиналдықты шектен тыс өсіріп, метрика қоймасын шамадан тыс жүктеуі мүмкін. Корреляция үшін ID мәнін тек баяу немесе қате сұрауларда логқа жазыңыз, ал метрикаларды сценарий, модель, аймақ және нәтиже класы бойынша жинақтаңыз.

k6 бірінші байт пен толық жауапты бөлек өлшеуге тиіс

Grafana k6 құжаттамасы Response.timings.waiting мәнін сұрау жіберілгеннен кейін жауапты күту фазасы деп, ал duration мәнін жіберу, күту және қабылдау қосындысы деп сипаттайды. Ағынсыз API үшін waiting бірінші байтқа дейінгі уақытқа жақын. SSE қолдансаңыз, k6 нұсқаңыздың әрекетін алдымен шағын тестпен тексеріңіз: кейбір клиент жолдары денені буферге жинайды, метриканың әдемі атауы нақты іске асыруды өзгерте алмайды.

Төменде пайдаланушы Trend метрикалары бар ең аз тест берілген. Кіріс ұзындығы өзгермеуі үшін ол тұрақты prompt жібереді және клиенттік конвертті, TTFT пен толық ұзақтықты жазады. URL мен токен орта айнымалыларынан келеді, сондықтан құпия кодқа да, тегтерге де түспейді.

import http from 'k6/http';
import { check } from 'k6';
import { Trend, Counter } from 'k6/metrics';
import { traceContext } from './trace.js';

const ttft = new Trend('llm_ttft_ms', true);
const total = new Trend('llm_total_ms', true);
const transport = new Trend('llm_transport_setup_ms', true);
const failures = new Counter('llm_failures');

export const options = {
  scenarios: {
    steady: {
      executor: 'constant-arrival-rate',
      rate: 8,
      timeUnit: '1s',
      duration: '10m',
      preAllocatedVUs: 40,
      maxVUs: 120,
    },
  },
  thresholds: {
    'llm_ttft_ms{scenario:steady}': ['p(95)<1200'],
    'llm_total_ms{scenario:steady}': ['p(95)<6000'],
    'llm_failures{scenario:steady}': ['count<5'],
    dropped_iterations: ['count==0'],
  },
};

export default function () {
  const trace = traceContext();
  const payload = JSON.stringify({
    model: __ENV.MODEL,
    messages: [{ role: 'user', content: 'Объясни хеш-таблицу в 120 словах.' }],
    temperature: 0,
    max_tokens: 180,
    stream: false,
  });
  const res = http.post(`${__ENV.BASE_URL}/v1/chat/completions`, payload, {
    headers: {
      Authorization: `Bearer ${__ENV.API_TOKEN}`,
      'Content-Type': 'application/json',
      traceparent: trace.header,
    },
    tags: { endpoint: 'chat', model: __ENV.MODEL },
    timeout: '30s',
  });

  ttft.add(res.timings.waiting, { model: __ENV.MODEL });
  total.add(res.timings.duration, { model: __ENV.MODEL });
  transport.add(
    res.timings.blocked + res.timings.connecting + res.timings.tls_handshaking,
    { model: __ENV.MODEL },
  );

  const ok = check(res, {
    'status 200': (r) => r.status === 200,
    'response has id': (r) => Boolean(r.json('id')),
  });
  if (!ok) {
    failures.add(1);
    console.error(JSON.stringify({ trace_id: trace.traceId, status: res.status }));
  }
}

Бұл нұсқа әдейі stream: false мәнінен басталады: ол SSE клиентіне қатысты белгісіздіктерсіз корреляция мен фаза бюджеттерін тексереді. Нақты streaming SLO үшін тек HTTP тақырыптарын емес, токені бар бірінші оқиғаны алған кезде монотондық уақыт белгісін тіркейтін клиентті қолданыңыз. k6 жүктеме генераторы болып қала алады, ал шағын үйлесімді клиентті төмен жиілікті бөлек probe ретінде іске қосуға болады. Екі әдісті бірдей сұраулармен салыстырмайынша, оның нәтижелерін негізгі қатарға қоспаңыз.

http_req_waiting мәнін серверлік генерация деп қабылдамаңыз. Метрика бірінші байтқа дейінгі барлық кезеңді көреді: серверге баратын жол, шлюз өңдеуі, кезек және inference басталуы. Бұл бөліктерді тек серверлік спандар ажырата алады.

Серверлік trace сұрау жолын қайталауға тиіс

Шлюздің түбірлік спаны сұрау қабылданған сәттен жауап толық жазылып біткенге дейінгі аралықты қамтуы керек. Ішкі спандар әр функцияға емес, жауапкершілік шекараларына сай құрылуы керек. Іс жүзінде мынадай схема ыңғайлы:

POST /v1/chat/completions          SERVER
  auth.check                       INTERNAL
  request.prepare                  INTERNAL
  route.select                     INTERNAL
  inference.acquire_slot           INTERNAL   llm.phase=queue
  inference.generate               CLIENT     llm.phase=generation
  response.write                   INTERNAL

Шлюз сыртқы inference API-ге жүгінсе, inference.generate kind мәні CLIENT болады және жауап оқылғаннан кейін аяқталады. Модель сол процесте жұмыс істесе, INTERNAL жарайды. OpenTelemetry Semantic Conventions HTTP және GenAI атрибуттарына ортақ атаулар береді, бірақ нақты жоспарлаушының жергілікті кезегі сіздің операциялық деталіңіз болып қалады. Оны модель шақыруының бір спанына жасырмаңыз, өйткені trace диагностикалық пайдасын жоғалтады.

Спан ұзақтығын SDK монотондық сағатымен жазыңыз. inference.acquire_slot спанына llm.model, llm.pool, llm.priority сияқты кардиналдығы төмен атрибуттарды және соңғы llm.queue.outcome мәнін қосыңыз. Генерацияға модель, шығыс токендерінің шегі, нақты токен саны, аяқталу себебі және streaming белгісі қажет. Backend индекстейтін атрибуттарға prompt, толық жауап, API кілті, email немесе жеке request ID салмаңыз.

Кезек шекарасын кодта нақты анықтаңыз. Мысалы, queue_start валидация мен пул таңдаудан кейін қойылады, ал generation_start жоспарлаушы слот берген сәтте дәл шақырылады. Провайдер тек жалпы жауап уақытын беріп, кезегін ашпаса, спанды provider.request деп атаңыз. Ішкі GPU queue уақытын азайту арқылы қалпына келтіре алмайсыз. Мұндай бағыт үшін шлюзге, сыртқы шақыруға және клиенттік конвертке ғана бюджет белгілеуге болады.

Токен есептегіштері генерацияны қалыпқа келтіруге көмектеседі. Модель қалыпты жұмыс істесе де, жауап ұзарған сайын толық p95 өседі, сондықтан жанында generation_ms_per_output_token және TTFT көрсеткіштерін қараңыз. Қате кезінде нөл немесе өте аз токен санын қалыпты нәтиже сияқты бөлмеңіз; қателерді бөлек класта ұстаңыз.

Ұзақтықтар айырмасы желілік конвертті береді

Маршруттау алдында PII бүркемелеңіз
Шлюз сұрауды таңдалған бағытпен жібермей тұрып жеке деректерді бүркемелейді.

k6 жазбасы мен түбірлік спанды trace ID арқылы сәйкестендіріп, network_envelope_ms = k6_duration_ms - server_root_duration_ms формуласын есептеңіз. Теріс нәтиже шекарада, өлшем бірлігінде немесе корреляцияда қате барын білдіреді. Ол желі сұрауды жылдамдатты деген сөз емес.

Формулаға екі машинаның абсолют start_time мәндері керек емес. NTP журналдарды уақыт бойынша қарауға жеткілікті жақындықты сақтай алады, бірақ дрейф пен секірмелі түзету шағын желілік бюджеттен оңай асып кетеді. Әр бақылаушы ұзақтықты өз монотондық сағатымен өлшейді, сондықтан ұзақтықтарды салыстыру сенімдірек. Түбірлік спан жауап денесі толық жіберілмей тұрып аяқталса, response.write қосып, түбірді нақты жазу аяқталғанға дейін созыңыз.

Ағынды жауап үшін екі жұп құрыңыз. Клиент TTFT мәнін сервердің сұрауды қабылдауынан бірінші токенді жазуға дейінгі уақытымен салыстырыңыз. Клиенттің толық ұзақтығын ағын жабылғанға дейінгі түбірлік спанмен салыстырыңыз. Сонда қалдықтар әртүрлі мағына береді: біріншісіне сұрау жолы мен алғашқы chunk кіреді, екіншісіне кейінгі chunk-тарды тасымалдау мен клиенттің оқуы да қосылады.

Агрегацияны екі тәуелсіз p95 мәнін азайту арқылы емес, әр сұрауды join жасағаннан кейін орындаңыз. Жалпы жағдайда p95(A) - p95(B) мәні p95(A - B) мәніне тең емес: әр үлестірімнің құйрығына әртүрлі сұраулар түседі. Алдымен әр сәйкес жұптың айырмасын есептеп, содан кейін жаңа қатардың p50, p95 және p99 мәндерін табыңыз. Қасына сәйкес келген traces үлесін жариялаңыз. Join сұраулардың 62%-ын ғана қамтыса, қалдықтың дәл p95 мәні көп нәрсені дәлелдемейді.

Бір сұрауды салыстыру түсінікті есеп беруі керек:

trace_id=0000000000000007000000000000012f
k6_total_ms=1842
server_root_ms=1691
network_envelope_ms=151
gateway_ms=37
gpu_queue_ms=428
generation_ms=1210
unattributed_server_ms=16

unattributed_server_ms түбірлік спан мен таңдалған ішкі фазалар қосындысының айырмасын көрсетеді. Шағын қалдық қалыпты, өйткені спандар арасында код орындалады. Өсіп жатқан қалдық жоғалған фазаны немесе қате шекараны білдіреді, оны үнсіз желіге қосуға болмайды.

Шектер фактіні емес, бюджетті белгілейді

Алдымен пайдаланушы SLO мәнін алыңыз, содан кейін уақытты компоненттерге бөліңіз. Шекті басқа мақаладан көшірмеңіз: модель, жауап ұзындығы, аймақ, concurrency және арна түрі үлестірімді өзгертеді. TTFT p95 көрсеткіші 1200 мс-тан аспауға тиіс сервис үшін бастапқы бюджет мынадай болуы мүмкін: желілік конвертке 120 мс, шлюзге 60 мс, GPU кезегіне 250 мс, генерация ішіндегі бірінші токенге дейін 770 мс. Бұл салалық норматив емес, арифметика үлгісі.

Әр бюджеттің шарты мен уақыт терезесі болуы керек. «Queue p95 250 мс-тан аз» деген сөйлем модель, аймақ, басымдық, кіріс токендерінің ауқымы, берілген қарқын және кемінде он минуттық тұрақты терезе көрсетілмесе, толық емес. Салқын іске қосуды бөлек сценарийде тексеріңіз. Әйтпесе сирек болатын салмақ жүктеулері жұмыс кезегімен араласып, команда қате режимді оңтайландырады.

Клиент шектері k6 ішінде тұрады, ал сервер шектерін көбіне telemetry backend немесе тесттен кейінгі бөлек тексеріс есептейді. Пайдалы тексеру жиынына мыналар кіреді:

  • network_envelope_ms p95 < 120, тек сәйкес traces үшін;
  • gateway_ms p95 < 60, сыртқы провайдерді күту уақытынсыз;
  • gpu_queue_ms p95 < 250, нақты пул мен сұрау класы үшін;
  • тұрақты токен профилінде ttft_ms p95 < 1200 және total_ms p95 < 6000;
  • қате үлесі, dropped iterations және сәтті сәйкестендірілген traces үлесі.

Жылдам қате latency көрсеткішін жақсартпауға тиіс. Сәтті жауаптардың квантильдерін бөлек есептеп, error rate мәнін жеке тексеру ретінде ұстаңыз. Клиент тоқтатқан сұрауларға да осы қағида қолданылады: бір секундтан кейін тоқтатылған сұрау жылдам генерация болып көрінбеуі керек.

Желі шегін setup пен қалдыққа бөліңіз. blocked + connecting + tls_handshaking қосылым пулы мен TLS орнату мәселелерін көрсетеді, ал жұпталған конверт тасымалдауды және сыртқы балансерлерді қамтиды. Setup өссе, қосылымдарды қайта пайдалануды және VU лимиттерін тексеріңіз. Setup тұрақты болып, конверт жауап көлемімен бірге өссе, GPU емес, арна немесе буферлеу күдік тудырады.

Ашық жүктеме моделі кезекті шынайы көрсетеді

Сыртқы және жергілікті inference салыстырыңыз
Бір API провайдер бағыттары мен жергілікті GPU инфрақұрылымындағы модельдерді ұсынады.

Қанығу нүктесін табу үшін сұраулардың келу жылдамдығын жауап уақытынан тәуелсіз орнатыңыз. k6 ішіндегі constant-arrival-rate итерацияларды кесте бойынша бастайды; баяу жауапқа көбірек VU керек, бірақ ол берілген қарқынды азайтпайды. VU саны тұрақты жабық модель latency өскенде arrival rate мәнін өзі төмендетіп, кезектің басталуын жасыруы мүмкін.

Бұл жерде dropped_iterations нәтиженің бір бөлігі. VU жетпегендіктен генератор жоспарланған итерацияларды бастай алмаса, тест уәде етілген жүктемені жасаған жоқ. Нақты ағын 5 RPS-ке дейін түссе, сервердің p95 мәнін 8 RPS үшін көрсетуге болмайды. preAllocatedVUs пен maxVUs мәндерін өсіріңіз немесе rate мәнін генератор ұстай алатын деңгейге азайтыңыз.

Кемінде екі бөлек прогон өткізіңіз. Күтілетін жұмыс қарқынындағы тұрақты прогон SLO талаптарын тексереді. Сатылы прогон arrival rate мәнін көтеріп, иілу нүктесін табады: кезек utilization көрсеткішінен жылдам өсе бастайды, ал throughput кіріс ағынға ілесе алмайды. Олардың квантильдерін біріктірмеңіз, себебі прогондар әртүрлі сұраққа жауап береді.

Кіріс және шығыс токендерінің үлестірімін бекітіңіз. Бір қысқа prompt инфрақұрылымды салыстыруға ыңғайлы, бірақ production жүктемесін нашар көрсетеді. Шынайы профиль үшін бірнеше ұзындық тобын дайындап, оларды белгіленген пропорциямен беріңіз. Әр нақты ұзындықты тегке салмаңыз; input_0_512 және output_129_256 сияқты ауқымдарды қолданыңыз, әйтпесе кардиналдық тағы бір инцидентке айналады.

Параллелизм мен RPS те бір ұғым емес. Бір минуттық генерациямен секундына екі сұрау көптеген слотты ұстайды, ал қысқа жауаптармен дәл сол RPS кезек құрмайды деуге болады. Есепте arrival rate жанында белсенді сұраулар, секундына токен саны, жоспарлаушының batch size мәні және GPU жүктемесі болуы керек. GPU жүктемесінің өзі жүйенің пайдаланушы күтуіне сай жұмыс істейтінін көрсетпейді.

График пішіні кідіріс иесін көрсетеді

Ең биік сызыққа қадалмай, фазалардың бірге қалай өзгеретінін бақылаңыз. Желілік конверттің, шлюздің, кезектің және генерацияның себептері бөлек, жүктеме өскенде олар әртүрлі әрекет етеді.

Әр симптомды ықтимал себеппен және алғашқы тексеріспен сәйкестендіріңіз:

  • Connect пен TLS өсіп, серверлік спандар тұрақты қалды | генератор жаңа қосылымдар ашады немесе жергілікті ресурс таусылды | қосылымды қайта пайдалану, сокет лимиттері, генератор CPU.
  • Gateway өсіп, queue тұрақты қалды | rate limit, сериализация, бүркемелеу немесе шлюз репликалары жетіспейді | шлюздің ішкі спандары және CPU.
  • Queue өсіп, бір токенге шаққан generation тұрақты қалды | inference пулы қанықты | concurrency, batch саясаты, кезек ұзындығы.
  • Бір токенге шаққан generation batch-пен бірге өсті | batch тым агрессивті немесе жады тапшы | batch size, GPU жады, токен профилі.
  • TTFT нашар, total өзгермеді деуге болады | басталғанға дейінгі күту өсті, ал нәтиже қысқа | queue мен prefill-ді бөлек қарау.
  • Client total өсіп, root span өспеді | жауапты тасымалдау немесе root span сыртындағы бөлік | root шекарасы, дене көлемі, балансер.

Команданы жиі шатастыратын ақауды қарайық. 6 RPS кезінде клиент p95 мәні 1,4 с, ал 9 RPS кезінде 3,8 с болады. Генерация уақыты шамамен 1,1 с болып қалады, шлюз 40 мс алады, кезек 180 мс-тан 2,5 с-қа өседі. Шлюзге timeout қосу баяу сәтті жауаптарды жылдам қателерге ғана айналдырады. Түзету пул сыйымдылығында, batch саясатында, шығыс токендерінің лимитінде немесе admission control ішінде жатыр.

Басқа жағдай клиент графигінде ұқсас көрінеді: p95 500 мс-қа өсті, бірақ серверлік фазалардың бәрі бұрынғыдай. Сонымен қатар tls_handshaking әр сұрауда дерлік пайда болады. Демек, тест қосылымдарды қайта пайдаланбай қалды немесе балансер keep-alive сессияларын жауып жатыр. GPU масштабтау мұнда қымбат әрі пайдасыз.

Гипотезаны бақыланатын өзгеріспен тексеріңіз. Arrival rate мәнін ұстап тұрып max tokens санын азайтыңыз да, generation мен queue қысқара ма, соны қараңыз. Серверді өзгертпей, генераторды оған жақынырақ іске қосып, желілік конвертті тексеріңіз. Әр прогонда бір факторды ғана өзгертіңіз, әйтпесе trace кідірістің құрамын көрсеткенімен, өзгеріс себебін дәлелдемейді.

Телеметрия өлшенетін жүйені өзгертпеуге тиіс

Қазақстандағы жергілікті inference таңдаңыз
AI Router ел ішіндегі өз GPU инфрақұрылымында 20+ open-weight модельді хосттайды.

Жүктеме кезінде әр спанды толық жазу Collector, экспорт желісі немесе backend мүмкіндігінен асып кетуі ықтимал. Сонда өлшеу контуры өзі кідіріс қосып, ең қызық traces жазбаларын қатар жоғалтады. Тест алдында exporter кезегін, dropped spans санын, Collector CPU мәнін және batch export уақытын бақылаңыз.

Head sampling баптау оңай, бірақ ол шешімді басында қабылдайды және сұраудың кейін баяулайтынын білмейді. OpenTelemetry sampling құжаттамасы осы шектеуді атап, trace ішіндегі аяқталған спандарды қарайтын tail sampling тәсілін ұсынады. Жүктеме терезесінде барлық қате мен ұзақтық шегінен асқан traces, сондай-ақ қалыпты сұраулардың шағын ықтималдық үлгісі сақталғаны дұрыс. Тест әр серверлік фазаның дәл үлестірімін талап етсе, sampling гистограмма метрикаларын алмастыра алмайды.

Метрикалар барлық сұрау бойынша тұрақты квантиль береді, ал traces құйрықтағы нақты жағдайларды түсіндіреді. Бір фазаны екі сигналмен де өлшеңіз: llm.queue.duration гистограммасы p95 құрады, ал inference.acquire_slot спаны нақты баяу сұрауды түсіндіреді. OpenTelemetry ұзақ орындалатын маңызды операцияға сол операцияның метрикасын қоса беруді ұсынады; бұл жағдайда ұсыным зерттеу уақытын шынымен қысқартады.

Join кезіндегі іріктеу ығысуын бақылаңыз. Backend тек баяу traces сақтаса, жұпталған желілік конверт бүкіл ағынды емес, құйрықты көрсетеді. Exemplar trace ID бар ықшам серверлік метриканы экспорттаңыз немесе бақыланатын прогон кезінде келісілген іріктеуді қосып, коэффициентін жазыңыз. Sampling тәсілі көрсетілмеген есепті қайталау мүмкін емес.

Фазаларды өлшеу үшін prompt пен жауап мазмұны қажет емес. Реттелетін ортада мұндай мазмұнды жазу дерек тарау қаупін туғызып, телеметрия көлемін өсіреді. Ұзындық ауқымдары, модель, статус, қате класы және сақтау мерзімі шектеулі техникалық идентификаторлар жеткілікті.

Бір есеп шешім қабылдауға жеткілікті болуы керек

Жақсы есеп тест конфигурациясын нәтижемен бірге сақтайды: k6 сценарийінің commit мәні, модель мен endpoint, токен профилі, arrival rate, генератор аймағы, streaming режимі, sampling ережелері және спан схемасының нұсқалары. Бұларсыз екі p95 мәнін салыстыру екі бөлек экспериментті салыстыруға айналады.

Бір экранда клиенттік TTFT пен total, жұпталған желілік конверт, gateway, GPU queue, generation, error rate, dropped iterations және join coverage болуы керек. p50, p95 және p99 мәндерін көрсетіңіз, бірақ шешімді алдын ала таңдалған SLO бойынша қабылдаңыз. Команда прогоннан кейін шекті нақты p95 мәнінен жоғары көтерсе, бұл тексеріс емес, нәтижені рәсімдеу болады.

Шектеулі прогондар үшін ұзақтықтардың бастапқы жұптарын да сақтаңыз. Агрегаттар тексеру шектеріне ыңғайлы, бірақ жаңа бюджетті қайта есептеуге немесе join қатесін кейін тексеруге мүмкіндік бермейді. Trace ID, статус, сұрау класы және фазалар сандары жеткілікті; сұрау денелері керек емес. Релиздерді бір жүктеме профилінде салыстырып, маршруттау, лимит немесе аппараттық пулдағы әр өзгерісті белгілеңіз. Әйтпесе жинақтар арасындағы айырма кодты емес, шарттардың ауысуын көрсетеді.

Модель маршрутизаторы үшін нақты провайдер мен модель бойынша бөлініс қосыңыз, бірақ бағыттарды бір ортақ құйрыққа араластырмаңыз. AI Router бір OpenAI-үйлесімді endpoint береді және сұрауларды түрлі модельдерге бағыттайды, сондықтан trace таңдалған бағытты prompt пен жеке дерексіз, кардиналдығы төмен атрибут ретінде сақтауы керек. Бұл бөлініс әдістемені бір провайдерге байламай, кідірісті жергілікті шлюз немесе сыртқы inference шақыруы қосқанын көрсетеді.

Дайындықтың бірінші шарты қарапайым: инженер кездейсоқ таңдалған баяу request үшін trace-ті бірнеше минутта тауып, ұзақтықтың басым бөлігін фазалар қосындысымен түсіндіре алады. Екінші шарт қатаңырақ: автоматты шектер тек жалпы p95 бойынша емес, өз бюджеті бұзылған фазада іске қосылады. Осы екі шарт орындалмайынша, кідіріс графигі симптомды көрсетеді де, түзету иесін атамайды.

Жиі қойылатын сұрақтар

Желі мен GPU уақытын тек k6 метрикаларымен бөлуге бола ма?

Жоқ. k6 клиенттік HTTP фазаларын көреді, бірақ сұраудың GPU слотын қашан күткенін немесе inference қашан басталғанын білмейді. Trace ID арқылы сұрауға байланысқан серверлік түбір спан мен ішкі спандар қажет.

Неліктен сервер p95 мәнін клиент p95 мәнінен азайтуға болмайды?

Екі үлестірімнің құйрығына әдетте әртүрлі сұраулар түседі. Алдымен әр trace үшін клиенттік және серверлік ұзақтықты сәйкестендіріп, айырмасын есептеңіз, содан кейін жаңа қатардың p95 мәнін табыңыз.

k6 ішіндегі http_req_waiting нақты нені көрсетеді?

Метрика клиент тұрғысынан сұрауды жіберу аяқталған сәттен жауаптың бірінші байтына дейінгі уақытты көрсетеді. Оған серверге жол, шлюз жұмысы, кезек және генерацияның басталуы кіреді, сондықтан ол GPU уақытына тең емес.

Ағынды LLM жауабы үшін TTFT қалай өлшенеді?

Клиент токені бар бірінші SSE оқиғасын алған сәтте монотондық уақыт белгісін қоюы керек. Оны сервердің бірінші токенді жазу уақытымен салыстырып, толық ұзақтықты ағын жабылғанға дейін бөлек өлшеңіз.

GPU кезегіне қандай спандар керек?

Дайын сұрау жоспарлаушыға берілген сәттен есептеу слоты бөлінгенге дейін жеке спан жасаңыз. Оған валидацияны, шлюз семафорын күтуді немесе генерацияның өзін қоспаңыз.

GPU кезегінің қандай p95 шегін қалыпты деуге болады?

Барлығына ортақ сан жоқ. Пайдаланушы SLO мәнінен бюджет бөліп, модельді, басымдықты, токен профилін, arrival rate пен өлшеу терезесін бекітіңіз; сонда шекті қайталап тексеруге болады.

k6 мен сервер сағаттарын синхрондау керек пе?

Ұзақтықтарды салыстыру үшін дәл синхрондау қажет емес, өйткені әр процесс өз монотондық сағатын қолданады. Синхрондау журналдарды уақыт бойынша қарауға ыңғайлы, бірақ абсолют timestamps мәндерін азайтуға болмайды.

Неліктен constant-arrival-rate тұрақты VU санынан жақсы?

Ол жауап уақыты өскенде де берілген сұрау қарқынын ұстап, жиналып жатқан кезекті көрсетеді. VU саны тұрақты болса, сұраулар баяулағанда нақты RPS төмендейді де, қанығу жасырын қалуы мүмкін.

Жүктеме тестінде барлық traces жазуға бола ма?

Тек telemetry контурының өткізу қабілетін тексергеннен кейін. Dropped spans пен exporter кезегін бақылаңыз; үлкен прогондарда гистограммаларды қателер мен баяу traces сақтайтын sampling тәсілімен біріктіріңіз.

Провайдер ішкі GPU кезегін көрсетпесе не істеу керек?

Белгісіз аралықты кезекке жатқызбаңыз. Жергілікті шлюзді, сыртқы шақырудың жалпы уақытын және клиенттік желілік конвертті өлшеп, провайдердің ішкі кезегін бақылауға келмейтін бөлік деп белгілеңіз.