Create LLM context memory calculator with:
- Accurate memory calculation using ggml quantization formulas - Support for f32, f16, bf16, q8_0, q4_0, q4_1, iq4_nl, q5_0, q5_1 quantizations - Asymmetric context support (separate K/V cache quantization) - Full attention interval support - Parallel sequences multiplier - Bilingual interface (Russian/English) - Retro-style design with tooltips Signed-off-by: Arseniy Romenskiy <romenskiy@altlinux.org> - Co-authored-by: Qwen3.5-35B-A3B-Claude-4.6-Opus-Reasoning-Distilled <qwen@example.com>
This commit is contained in:
commit
c471e1d0a9
4 changed files with 1007 additions and 0 deletions
326
css/styles.css
Normal file
326
css/styles.css
Normal file
|
|
@ -0,0 +1,326 @@
|
||||||
|
/*
|
||||||
|
* LLM Context Memory Calculator - Retro Style CSS
|
||||||
|
* White background, sharp borders, high contrast
|
||||||
|
*/
|
||||||
|
|
||||||
|
* {
|
||||||
|
box-sizing: border-box;
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: system-ui, -apple-system, sans-serif;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
color: #000000;
|
||||||
|
line-height: 1.6;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
border: 3px solid #000000;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #0000FF;
|
||||||
|
border-bottom: 2px solid #0000FF;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-btn {
|
||||||
|
background-color: #000000;
|
||||||
|
color: #FFFFFF;
|
||||||
|
border: 2px solid #000000;
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-btn:hover {
|
||||||
|
background-color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"],
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 8px;
|
||||||
|
border: 2px solid #000000;
|
||||||
|
background-color: #FFFFFF;
|
||||||
|
color: #000000;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"]:focus,
|
||||||
|
select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: #0000FF;
|
||||||
|
box-shadow: 0 0 5px rgba(0, 0, 255, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"]::placeholder {
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-group {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-group input[type="checkbox"] {
|
||||||
|
width: auto;
|
||||||
|
margin-right: 10px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.checkbox-group label {
|
||||||
|
margin-bottom: 0;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asymmetric-controls {
|
||||||
|
display: none;
|
||||||
|
border: 2px solid #000000;
|
||||||
|
padding: 15px;
|
||||||
|
margin-top: 10px;
|
||||||
|
background-color: #F5F5F5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asymmetric-controls.visible {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.asymmetric-controls .form-group {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 12px;
|
||||||
|
background-color: #000000;
|
||||||
|
color: #FFFFFF;
|
||||||
|
border: none;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results {
|
||||||
|
margin-top: 20px;
|
||||||
|
border: 2px solid #000000;
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.results h2 {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
color: #000000;
|
||||||
|
border-bottom: 1px solid #000000;
|
||||||
|
padding-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 5px 0;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-item::before {
|
||||||
|
content: '├─';
|
||||||
|
margin-right: 5px;
|
||||||
|
color: #000000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-item:last-child::before {
|
||||||
|
content: '└─';
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-item .label {
|
||||||
|
color: #333333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-item .value {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-item.total::before {
|
||||||
|
content: '└─';
|
||||||
|
color: #0000FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-item.total .value {
|
||||||
|
color: #0000FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example {
|
||||||
|
margin-top: 20px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #CCCCCC;
|
||||||
|
background-color: #FAFAFA;
|
||||||
|
}
|
||||||
|
|
||||||
|
.example p {
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #666666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
background-color: #FFEEEE;
|
||||||
|
border: 2px solid #FF0000;
|
||||||
|
color: #FF0000;
|
||||||
|
padding: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message ul {
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-message li {
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tooltip Styles */
|
||||||
|
.tooltip-icon {
|
||||||
|
display: inline-block;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
background-color: #000000;
|
||||||
|
color: #FFFFFF;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 16px;
|
||||||
|
font-family: 'Courier New', monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-left: 5px;
|
||||||
|
cursor: help;
|
||||||
|
vertical-align: middle;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-icon::before {
|
||||||
|
content: "?";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide both languages by default */
|
||||||
|
.tooltip-icon[data-tooltip-ru],
|
||||||
|
.tooltip-icon[data-tooltip-en] {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show Russian when html lang is ru */
|
||||||
|
html[lang="ru"] .tooltip-icon[data-tooltip-ru] {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show English when html lang is en */
|
||||||
|
html[lang="en"] .tooltip-icon[data-tooltip-en] {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tooltip-icon:hover {
|
||||||
|
background-color: #0000FF;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tooltip popup for Russian */
|
||||||
|
html[lang="ru"] .tooltip-icon:hover::after {
|
||||||
|
content: attr(data-tooltip-ru);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background-color: #000000;
|
||||||
|
color: #FFFFFF;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 11px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
width: 375px;
|
||||||
|
z-index: 1000;
|
||||||
|
line-height: 1.4;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tooltip popup for English */
|
||||||
|
html[lang="en"] .tooltip-icon:hover::after {
|
||||||
|
content: attr(data-tooltip-en);
|
||||||
|
position: absolute;
|
||||||
|
bottom: 100%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%);
|
||||||
|
background-color: #000000;
|
||||||
|
color: #FFFFFF;
|
||||||
|
padding: 12px 16px;
|
||||||
|
font-size: 11px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
width: 375px;
|
||||||
|
z-index: 1000;
|
||||||
|
line-height: 1.4;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 600px) {
|
||||||
|
.container {
|
||||||
|
padding: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="number"],
|
||||||
|
select {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lang-btn {
|
||||||
|
align-self: flex-end;
|
||||||
|
}
|
||||||
|
}
|
||||||
118
index.html
Normal file
118
index.html
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="ru">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title data-key="title">Калькулятор Памяти Контекста LLM</title>
|
||||||
|
<link rel="stylesheet" href="css/styles.css">
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<div class="header">
|
||||||
|
<h1 data-key="title">Калькулятор Памяти Контекста LLM</h1>
|
||||||
|
<button id="lang-toggle" class="lang-btn">EN</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<form id="calculator-form">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="context-length" data-key="context_length_label">Длина Контекста (токены)</label>
|
||||||
|
<input type="number" id="context-length" placeholder="Введите длину контекста" required min="1">
|
||||||
|
<span class="tooltip-icon" data-tooltip-ru="Количество токенов в контексте. Влияет линейно на размер KV кеша" data-tooltip-en="Number of tokens in context. Affects KV cache size linearly"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="k-type" data-key="k_type_label">Квантование K кеша</label>
|
||||||
|
<select id="k-type" required>
|
||||||
|
<option value="KV" selected data-key="kv_cache">KV кеш</option>
|
||||||
|
<option value="f32">f32</option>
|
||||||
|
<option value="f16">f16</option>
|
||||||
|
<option value="bf16">bf16</option>
|
||||||
|
<option value="q8_0" data-key="q8_0">q8_0</option>
|
||||||
|
<option value="q4_0" data-key="q4_0">q4_0</option>
|
||||||
|
<option value="q4_1" data-key="q4_1">q4_1</option>
|
||||||
|
<option value="iq4_nl" data-key="iq4_nl">iq4_nl</option>
|
||||||
|
<option value="q5_0" data-key="q5_0">q5_0</option>
|
||||||
|
<option value="q5_1" data-key="q5_1">q5_1</option>
|
||||||
|
</select>
|
||||||
|
<span class="tooltip-icon" data-tooltip-ru="Тип квантования для тензоров ключей внимания. Меньшее значение = меньше памяти, но потенциально выше ошибка квантования" data-tooltip-en="Quantization type for attention key tensors. Lower value = less memory, but potentially higher quantization error"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="kv-heads" data-key="kv_heads_label">Головок KV</label>
|
||||||
|
<input type="number" id="kv-heads" placeholder="Введите количество головок KV" required min="1">
|
||||||
|
<span class="tooltip-icon" data-tooltip-ru="Количество голов внимания для K и V тензоров. Обычно num_key_value_heads в конфигурации модели" data-tooltip-en="Number of attention heads for K and V tensors. Usually num_key_value_heads in model config"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="head-size" data-key="head_size_label">Размер Головки</label>
|
||||||
|
<input type="number" id="head-size" placeholder="Введите размер головы" required min="1">
|
||||||
|
<span class="tooltip-icon" data-tooltip-ru="Размер каждой головы внимания (head_dim). Влияет линейно на размер KV кеша" data-tooltip-en="Size of each attention head (head_dim). Affects KV cache size linearly"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="num-heads" data-key="num_heads_label">Количество Головок</label>
|
||||||
|
<input type="number" id="num-heads" placeholder="Введите количество головок" required min="1">
|
||||||
|
<span class="tooltip-icon" data-tooltip-ru="Общее количество голов внимания. Используется только для информации и примеров" data-tooltip-en="Total number of attention heads. Used for display and examples only"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="num-layers" data-key="num_layers_label">Количество Слов</label>
|
||||||
|
<input type="number" id="num-layers" placeholder="Введите количество слоев" required min="1">
|
||||||
|
<span class="tooltip-icon" data-tooltip-ru="Количество слоев модели. Каждый слой имеет свой KV кеш" data-tooltip-en="Number of model layers. Each layer has its own KV cache"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="model-size" data-key="model_size_label">Размер Модели (GB)</label>
|
||||||
|
<input type="number" id="model-size" placeholder="Опционально, для общего объема памяти" min="0" step="0.1">
|
||||||
|
<span class="tooltip-icon" data-tooltip-ru="Размер весов модели в гигабайтах. Используется для расчета общего объема памяти (KV кеш + веса)" data-tooltip-en="Model weights size in gigabytes. Used to calculate total memory (KV cache + weights)"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="parallel" data-key="parallel_label">Параллелизм (np)</label>
|
||||||
|
<input type="number" id="parallel" value="1" min="1" title="">
|
||||||
|
<span class="tooltip-icon" data-tooltip-ru="Количество параллельных последовательностей для декодирования (-np/--parallel в llama.cpp). Увеличивает KV кеш пропорционально" data-tooltip-en="Number of parallel sequences for decoding (-np/--parallel in llama.cpp). Increases KV cache proportionally"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="full-attention" data-key="full_attention_label">Интервал Полного Внимания</label>
|
||||||
|
<input type="number" id="full-attention" placeholder="Опционально" min="1">
|
||||||
|
<span class="tooltip-icon" data-tooltip-ru="Интервал слоев для полного внимания. Слои считают полный KV кеш каждые N слоев. Уменьшает эффективное количество слоев" data-tooltip-en="Interval for full attention layers. Layers compute full KV cache every N layers. Reduces effective layer count"></span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="checkbox-group">
|
||||||
|
<input type="checkbox" id="asymmetric">
|
||||||
|
<label for="asymmetric" data-key="asymmetric_label">Асимметричный Контекст</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="asymmetric-controls" class="asymmetric-controls">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="v-type" data-key="v_type_label">Квантование V кеша</label>
|
||||||
|
<select id="v-type">
|
||||||
|
<option value="f32">f32</option>
|
||||||
|
<option value="f16">f16</option>
|
||||||
|
<option value="bf16">bf16</option>
|
||||||
|
<option value="q8_0" data-key="q8_0">q8_0</option>
|
||||||
|
<option value="q4_0" data-key="q4_0">q4_0</option>
|
||||||
|
<option value="q4_1" data-key="q4_1">q4_1</option>
|
||||||
|
<option value="iq4_nl" data-key="iq4_nl">iq4_nl</option>
|
||||||
|
<option value="q5_0" data-key="q5_0">q5_0</option>
|
||||||
|
<option value="q5_1" data-key="q5_1">q5_1</option>
|
||||||
|
</select>
|
||||||
|
<span class="tooltip-icon" data-tooltip-ru="Тип квантования для тензоров значений внимания. Можно выбрать отдельно от K кеша" data-tooltip-en="Quantization type for attention value tensors. Can be selected separately from K cache"></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="button" id="calculate-btn" data-key="calculate_btn">Рассчитать</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<div id="example-text" class="example">
|
||||||
|
<p data-key="example_text">Пример: context=8192, layers=32, kv_heads=32, head_size=128, model_size=7 GB, parallel=1</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="results"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="js/calculation.js"></script>
|
||||||
|
<script src="js/app.js"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
346
js/app.js
Normal file
346
js/app.js
Normal file
|
|
@ -0,0 +1,346 @@
|
||||||
|
/**
|
||||||
|
* LLM Context Memory Calculator - Main Application
|
||||||
|
* DOM manipulation and event handling
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
// Translation dictionary
|
||||||
|
const translations = {
|
||||||
|
ru: {
|
||||||
|
title: 'Калькулятор Памяти Контекста LLM',
|
||||||
|
context_length_label: 'Длина Контекста (токены)',
|
||||||
|
k_type_label: 'Квантование K кеша',
|
||||||
|
kv_cache: 'KV кеш',
|
||||||
|
q8_0: 'q8_0',
|
||||||
|
q4_0: 'q4_0',
|
||||||
|
q4_1: 'q4_1',
|
||||||
|
iq4_nl: 'iq4_nl',
|
||||||
|
q5_0: 'q5_0',
|
||||||
|
q5_1: 'q5_1',
|
||||||
|
kv_heads_label: 'Головок KV',
|
||||||
|
head_size_label: 'Размер Головки',
|
||||||
|
num_heads_label: 'Количество Головок',
|
||||||
|
num_layers_label: 'Количество Слов',
|
||||||
|
model_size_label: 'Размер Модели (GB)',
|
||||||
|
parallel_label: 'Параллелизм (np)',
|
||||||
|
full_attention_label: 'Интервал Полного Внимания',
|
||||||
|
asymmetric_label: 'Асимметричный Контекст',
|
||||||
|
v_type_label: 'Квантование V кеша',
|
||||||
|
calculate_btn: 'Рассчитать',
|
||||||
|
example_text: 'Пример: context=8192, layers=32, kv_heads=32, head_size=128, model_size=7 GB, parallel=1',
|
||||||
|
results_title: 'Результаты',
|
||||||
|
k_cache: 'K кеш:',
|
||||||
|
v_cache: 'V кеш:',
|
||||||
|
total_kv: 'Total KV:',
|
||||||
|
total: 'Total:',
|
||||||
|
errors_header: 'Ошибки:',
|
||||||
|
error_context_length: 'Длина контекста должна быть положительным числом',
|
||||||
|
error_kv_heads: 'Количество KV головок должно быть положительным числом',
|
||||||
|
error_head_size: 'Размер головы должен быть положительным числом',
|
||||||
|
error_num_layers: 'Количество слоев должно быть положительным числом',
|
||||||
|
error_full_attention: 'Интервал полного внимания должен быть положительным числом',
|
||||||
|
error_model_size: 'Размер модели должен быть положительным числом'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
title: 'LLM Context Memory Calculator',
|
||||||
|
context_length_label: 'Context Length (tokens)',
|
||||||
|
k_type_label: 'K cache quantization',
|
||||||
|
kv_cache: 'KV cache',
|
||||||
|
q8_0: 'q8_0',
|
||||||
|
q4_0: 'q4_0',
|
||||||
|
q4_1: 'q4_1',
|
||||||
|
iq4_nl: 'iq4_nl',
|
||||||
|
q5_0: 'q5_0',
|
||||||
|
q5_1: 'q5_1',
|
||||||
|
kv_heads_label: 'KV Heads',
|
||||||
|
head_size_label: 'Head Size',
|
||||||
|
num_heads_label: 'Number of Heads',
|
||||||
|
num_layers_label: 'Number of Layers',
|
||||||
|
model_size_label: 'Model Size (GB)',
|
||||||
|
parallel_label: 'Parallel (np)',
|
||||||
|
full_attention_label: 'Full Attention Interval',
|
||||||
|
asymmetric_label: 'Asymmetric Context',
|
||||||
|
v_type_label: 'V cache quantization',
|
||||||
|
calculate_btn: 'Calculate',
|
||||||
|
example_text: 'Example: context=8192, layers=32, kv_heads=32, head_size=128, model_size=7 GB, parallel=1',
|
||||||
|
results_title: 'Results',
|
||||||
|
k_cache: 'K cache:',
|
||||||
|
v_cache: 'V cache:',
|
||||||
|
total_kv: 'Total KV:',
|
||||||
|
total: 'Total:',
|
||||||
|
errors_header: 'Errors:',
|
||||||
|
error_context_length: 'Context length must be a positive number',
|
||||||
|
error_kv_heads: 'KV heads must be a positive number',
|
||||||
|
error_head_size: 'Head size must be a positive number',
|
||||||
|
error_num_layers: 'Number of layers must be a positive number',
|
||||||
|
error_full_attention: 'Full attention interval must be a positive number',
|
||||||
|
error_model_size: 'Model size must be a positive number'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Tooltip translations
|
||||||
|
const tooltips = {
|
||||||
|
ru: {
|
||||||
|
context_length: 'Количество токенов в контексте. Влияет линейно на размер KV кеша',
|
||||||
|
k_type: 'Тип квантования для тензоров ключей внимания. Меньшее значение = меньше памяти, но потенциально выше ошибка квантования',
|
||||||
|
kv_heads: 'Количество голов внимания для K и V тензоров. Обычно num_key_value_heads в конфигурации модели',
|
||||||
|
head_size: 'Размер каждой головы внимания (head_dim). Влияет линейно на размер KV кеша',
|
||||||
|
num_heads: 'Общее количество голов внимания. Используется только для информации и примеров',
|
||||||
|
num_layers: 'Количество слоев модели. Каждый слой имеет свой KV кеш',
|
||||||
|
model_size: 'Размер весов модели в гигабайтах. Используется для расчета общего объема памяти (KV кеш + веса)',
|
||||||
|
parallel: 'Количество параллельных последовательностей для декодирования (-np/--parallel в llama.cpp). Увеличивает KV кеш пропорционально',
|
||||||
|
full_attention: 'Интервал слоев для полного внимания. Слои считают полный KV кеш каждые N слоев. Уменьшает эффективное количество слоев',
|
||||||
|
v_type: 'Тип квантования для тензоров значений внимания. Можно выбрать отдельно от K кеша'
|
||||||
|
},
|
||||||
|
en: {
|
||||||
|
context_length: 'Number of tokens in context. Affects KV cache size linearly',
|
||||||
|
k_type: 'Quantization type for attention key tensors. Lower value = less memory, but potentially higher quantization error',
|
||||||
|
kv_heads: 'Number of attention heads for K and V tensors. Usually num_key_value_heads in model config',
|
||||||
|
head_size: 'Size of each attention head (head_dim). Affects KV cache size linearly',
|
||||||
|
num_heads: 'Total number of attention heads. Used for display and examples only',
|
||||||
|
num_layers: 'Number of model layers. Each layer has its own KV cache',
|
||||||
|
model_size: 'Model weights size in gigabytes. Used to calculate total memory (KV cache + weights)',
|
||||||
|
parallel: 'Number of parallel sequences for decoding (-np/--parallel in llama.cpp). Increases KV cache proportionally',
|
||||||
|
full_attention: 'Interval for full attention layers. Layers compute full KV cache every N layers. Reduces effective layer count',
|
||||||
|
v_type: 'Quantization type for attention value tensors. Can be selected separately from K cache'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Current language
|
||||||
|
let currentLang = 'ru';
|
||||||
|
|
||||||
|
// DOM Elements
|
||||||
|
const form = document.getElementById('calculator-form');
|
||||||
|
const kTypeSelect = document.getElementById('k-type');
|
||||||
|
const vTypeSelect = document.getElementById('v-type');
|
||||||
|
const asymmetricCheckbox = document.getElementById('asymmetric');
|
||||||
|
const asymmetricControls = document.getElementById('asymmetric-controls');
|
||||||
|
const calculateBtn = document.getElementById('calculate-btn');
|
||||||
|
const resultsContainer = document.getElementById('results');
|
||||||
|
const exampleText = document.getElementById('example-text');
|
||||||
|
const langToggleBtn = document.getElementById('lang-toggle');
|
||||||
|
|
||||||
|
// Initialize
|
||||||
|
function init() {
|
||||||
|
setupEventListeners();
|
||||||
|
updateText();
|
||||||
|
updateTooltips();
|
||||||
|
updateExampleText();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up event listeners
|
||||||
|
function setupEventListeners() {
|
||||||
|
asymmetricCheckbox.addEventListener('change', toggleAsymmetric);
|
||||||
|
calculateBtn.addEventListener('click', handleCalculate);
|
||||||
|
|
||||||
|
// Auto-calculate on input change
|
||||||
|
form.addEventListener('input', debounce(handleCalculate, 500));
|
||||||
|
|
||||||
|
// Language toggle
|
||||||
|
langToggleBtn.addEventListener('click', toggleLanguage);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle language
|
||||||
|
function toggleLanguage() {
|
||||||
|
currentLang = currentLang === 'ru' ? 'en' : 'ru';
|
||||||
|
langToggleBtn.textContent = currentLang === 'ru' ? 'EN' : 'RU';
|
||||||
|
document.documentElement.lang = currentLang;
|
||||||
|
updateText();
|
||||||
|
updateTooltips();
|
||||||
|
updateExampleText();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update all translatable text
|
||||||
|
function updateText() {
|
||||||
|
// Translate elements with data-key attribute
|
||||||
|
document.querySelectorAll('[data-key]').forEach(el => {
|
||||||
|
const key = el.getAttribute('data-key');
|
||||||
|
if (translations[currentLang][key]) {
|
||||||
|
if (el.tagName === 'TITLE') {
|
||||||
|
document.title = translations[currentLang][key];
|
||||||
|
} else {
|
||||||
|
el.textContent = translations[currentLang][key];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Update placeholder text
|
||||||
|
document.getElementById('context-length').placeholder =
|
||||||
|
currentLang === 'ru' ? 'Введите длину контекста' : 'Enter context length';
|
||||||
|
document.getElementById('kv-heads').placeholder =
|
||||||
|
currentLang === 'ru' ? 'Введите количество головок KV' : 'Enter KV heads';
|
||||||
|
document.getElementById('head-size').placeholder =
|
||||||
|
currentLang === 'ru' ? 'Введите размер головы' : 'Enter head size';
|
||||||
|
document.getElementById('num-heads').placeholder =
|
||||||
|
currentLang === 'ru' ? 'Введите количество головок' : 'Enter number of heads';
|
||||||
|
document.getElementById('num-layers').placeholder =
|
||||||
|
currentLang === 'ru' ? 'Введите количество слоев' : 'Enter number of layers';
|
||||||
|
document.getElementById('model-size').placeholder =
|
||||||
|
currentLang === 'ru' ? 'Опционально, для общего объема памяти' : 'Optional, for total memory';
|
||||||
|
document.getElementById('full-attention').placeholder =
|
||||||
|
currentLang === 'ru' ? 'Опционально' : 'Optional';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update tooltips
|
||||||
|
function updateTooltips() {
|
||||||
|
const tooltipMap = {
|
||||||
|
'context-length': 'context_length',
|
||||||
|
'k-type': 'k_type',
|
||||||
|
'kv-heads': 'kv_heads',
|
||||||
|
'head-size': 'head_size',
|
||||||
|
'num-heads': 'num_heads',
|
||||||
|
'num-layers': 'num_layers',
|
||||||
|
'model-size': 'model_size',
|
||||||
|
'parallel': 'parallel',
|
||||||
|
'full-attention': 'full_attention',
|
||||||
|
'v-type': 'v_type'
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(tooltipMap).forEach(fieldId => {
|
||||||
|
const tooltipIcon = document.querySelector(`#${fieldId} + .tooltip-icon`);
|
||||||
|
if (tooltipIcon) {
|
||||||
|
const key = tooltipMap[fieldId];
|
||||||
|
const ruText = tooltips.ru[key];
|
||||||
|
const enText = tooltips.en[key];
|
||||||
|
|
||||||
|
if (currentLang === 'ru') {
|
||||||
|
tooltipIcon.setAttribute('data-tooltip-ru', ruText);
|
||||||
|
tooltipIcon.removeAttribute('data-tooltip-en');
|
||||||
|
} else {
|
||||||
|
tooltipIcon.setAttribute('data-tooltip-en', enText);
|
||||||
|
tooltipIcon.removeAttribute('data-tooltip-ru');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update example text
|
||||||
|
function updateExampleText() {
|
||||||
|
const kvHeads = parseInt(document.getElementById('kv-heads').value) || 32;
|
||||||
|
const headSize = parseInt(document.getElementById('head-size').value) || 128;
|
||||||
|
const layers = parseInt(document.getElementById('num-layers').value) || 32;
|
||||||
|
const modelSize = document.getElementById('model-size').value || 7;
|
||||||
|
const context = parseInt(document.getElementById('context-length').value) || 8192;
|
||||||
|
const parallel = parseInt(document.getElementById('parallel').value) || 1;
|
||||||
|
|
||||||
|
exampleText.textContent = translations[currentLang].example_text
|
||||||
|
.replace('8192', context)
|
||||||
|
.replace('32', layers)
|
||||||
|
.replace('kv_heads=32', `kv_heads=${kvHeads}`)
|
||||||
|
.replace('head_size=128', `head_size=${headSize}`)
|
||||||
|
.replace('7 GB', `${modelSize} GB`)
|
||||||
|
.replace('parallel=1', `parallel=${parallel}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Toggle asymmetric context controls
|
||||||
|
function toggleAsymmetric() {
|
||||||
|
if (asymmetricCheckbox.checked) {
|
||||||
|
asymmetricControls.classList.add('visible');
|
||||||
|
const kvOption = kTypeSelect.querySelector('option[value="KV"]');
|
||||||
|
if (kvOption) {
|
||||||
|
kvOption.value = 'K';
|
||||||
|
kvOption.textContent = currentLang === 'ru' ? 'K кеш' : 'K cache';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
asymmetricControls.classList.remove('visible');
|
||||||
|
const kOption = kTypeSelect.querySelector('option[value="K"]');
|
||||||
|
if (kOption) {
|
||||||
|
kOption.value = 'KV';
|
||||||
|
kOption.textContent = translations[currentLang].kv_cache;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle calculate button click
|
||||||
|
function handleCalculate() {
|
||||||
|
const params = collectParams();
|
||||||
|
const validation = validateParams(params, currentLang);
|
||||||
|
|
||||||
|
if (!validation.valid) {
|
||||||
|
displayErrors(validation.errors);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const results = calculateMemory(params);
|
||||||
|
displayResults(results);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect parameters from form
|
||||||
|
function collectParams() {
|
||||||
|
return {
|
||||||
|
contextLength: parseInt(document.getElementById('context-length').value),
|
||||||
|
kType: asymmetricCheckbox.checked
|
||||||
|
? kTypeSelect.value === 'K' ? 'KV' : kTypeSelect.value
|
||||||
|
: 'KV',
|
||||||
|
vType: asymmetricCheckbox.checked ? vTypeSelect.value : null,
|
||||||
|
kvHeads: parseInt(document.getElementById('kv-heads').value),
|
||||||
|
headSize: parseInt(document.getElementById('head-size').value),
|
||||||
|
numLayers: parseInt(document.getElementById('num-layers').value),
|
||||||
|
modelSizeGB: parseFloat(document.getElementById('model-size').value) || null,
|
||||||
|
parallel: parseInt(document.getElementById('parallel').value) || 1,
|
||||||
|
fullAttentionInterval: document.getElementById('full-attention').value
|
||||||
|
? parseInt(document.getElementById('full-attention').value)
|
||||||
|
: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display calculation results
|
||||||
|
function displayResults(results) {
|
||||||
|
let html = '<div class="results"><h2>' + translations[currentLang].results_title + '</h2>';
|
||||||
|
|
||||||
|
html += `<div class="result-item"><span class="label">${translations[currentLang].k_cache}</span><span class="value">${results.kCache.formatted}</span></div>`;
|
||||||
|
html += `<div class="result-item"><span class="label">${translations[currentLang].v_cache}</span><span class="value">${results.vCache.formatted}</span></div>`;
|
||||||
|
html += `<div class="result-item"><span class="label">${translations[currentLang].total_kv}</span><span class="value">${results.totalKVCache.formatted}</span></div>`;
|
||||||
|
|
||||||
|
if (results.totalMemory) {
|
||||||
|
html += `<div class="result-item total"><span class="label">${translations[currentLang].total}</span><span class="value">${results.totalMemory.formatted}</span></div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
html += '</div>';
|
||||||
|
|
||||||
|
resultsContainer.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display validation errors
|
||||||
|
function displayErrors(errors) {
|
||||||
|
let html = '<div class="error-message"><strong>' + translations[currentLang].errors_header + '</strong><ul>';
|
||||||
|
errors.forEach(error => {
|
||||||
|
html += `<li>${error}</li>`;
|
||||||
|
});
|
||||||
|
html += '</ul></div>';
|
||||||
|
resultsContainer.innerHTML = html;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debounce function for performance
|
||||||
|
function debounce(func, wait) {
|
||||||
|
let timeout;
|
||||||
|
return function executedFunction(...args) {
|
||||||
|
const later = () => {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
func(...args);
|
||||||
|
};
|
||||||
|
clearTimeout(timeout);
|
||||||
|
timeout = setTimeout(later, wait);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Expose functions globally for testing
|
||||||
|
window.CalculatorApp = {
|
||||||
|
collectParams,
|
||||||
|
handleCalculate,
|
||||||
|
toggleAsymmetric,
|
||||||
|
updateExampleText,
|
||||||
|
toggleLanguage,
|
||||||
|
updateText,
|
||||||
|
updateTooltips
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize when DOM is ready
|
||||||
|
if (document.readyState === 'loading') {
|
||||||
|
document.addEventListener('DOMContentLoaded', init);
|
||||||
|
} else {
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
})();
|
||||||
217
js/calculation.js
Normal file
217
js/calculation.js
Normal file
|
|
@ -0,0 +1,217 @@
|
||||||
|
/**
|
||||||
|
* LLM Context Memory Calculator - Core Calculation Logic
|
||||||
|
* Pure functions with no DOM dependencies
|
||||||
|
*/
|
||||||
|
|
||||||
|
const QUANT_SIZES = {
|
||||||
|
f32: 4.0,
|
||||||
|
f16: 2.0,
|
||||||
|
bf16: 2.0,
|
||||||
|
q8_0: 34/32, // 1.0625
|
||||||
|
q4_0: 18/32, // 0.5625
|
||||||
|
q4_1: 20/32, // 0.625
|
||||||
|
iq4_nl: 18/32, // 0.5625
|
||||||
|
q5_0: 22/32, // 0.6875
|
||||||
|
q5_1: 24/32 // 0.75
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Normalize quantization type (handle 'K' alias for 'KV')
|
||||||
|
* @param {string} type - Quantization type
|
||||||
|
* @returns {string} Normalized type
|
||||||
|
*/
|
||||||
|
function normalizeQuantType(type) {
|
||||||
|
if (type === 'K') return 'KV';
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get quantization size in bytes per tensor element
|
||||||
|
* @param {string} type - Quantization type
|
||||||
|
* @returns {number} Size in bytes
|
||||||
|
*/
|
||||||
|
function getQuantizationSize(type) {
|
||||||
|
const normalizedType = normalizeQuantType(type);
|
||||||
|
return QUANT_SIZES[normalizedType] || QUANT_SIZES.f32;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Format bytes to human-readable string
|
||||||
|
* @param {number} bytes - Number of bytes
|
||||||
|
* @returns {string} Formatted string (e.g., "1.23 MB")
|
||||||
|
*/
|
||||||
|
function formatBytes(bytes) {
|
||||||
|
if (bytes === 0) return '0 B';
|
||||||
|
|
||||||
|
const k = 1024;
|
||||||
|
const sizes = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||||
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
||||||
|
|
||||||
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate memory breakdown
|
||||||
|
* @param {Object} params - Calculation parameters
|
||||||
|
* @returns {Object} Memory breakdown object
|
||||||
|
*/
|
||||||
|
function calculateMemory(params) {
|
||||||
|
const {
|
||||||
|
contextLength,
|
||||||
|
kType,
|
||||||
|
vType,
|
||||||
|
kvHeads,
|
||||||
|
headSize,
|
||||||
|
numLayers,
|
||||||
|
modelSizeGB,
|
||||||
|
parallel,
|
||||||
|
fullAttentionInterval
|
||||||
|
} = params;
|
||||||
|
|
||||||
|
// Calculate effective layers (account for full attention interval)
|
||||||
|
const effectiveLayers = fullAttentionInterval
|
||||||
|
? Math.ceil(numLayers / fullAttentionInterval)
|
||||||
|
: numLayers;
|
||||||
|
|
||||||
|
// Determine parallel multiplier (default 1)
|
||||||
|
const parallelMultiplier = parallel || 1;
|
||||||
|
|
||||||
|
// Get quantization sizes
|
||||||
|
const bK = getQuantizationSize(kType);
|
||||||
|
const bV = vType ? getQuantizationSize(vType) : bK;
|
||||||
|
|
||||||
|
// Memory per token for all layers
|
||||||
|
// Formula: ctx × layers × kvheads × headdim × (bK + bV)
|
||||||
|
const memoryPerToken = contextLength * effectiveLayers * kvHeads * headSize * (bK + bV);
|
||||||
|
|
||||||
|
// Total KV cache memory
|
||||||
|
const totalKVCache = memoryPerToken * parallelMultiplier;
|
||||||
|
|
||||||
|
// Total memory including model weights (if provided)
|
||||||
|
const totalMemory = modelSizeGB
|
||||||
|
? totalKVCache + (modelSizeGB * 1024 * 1024 * 1024)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
// Calculate individual cache sizes
|
||||||
|
const kCacheSize = contextLength * effectiveLayers * kvHeads * headSize * bK * parallelMultiplier;
|
||||||
|
const vCacheSize = contextLength * effectiveLayers * kvHeads * headSize * bV * parallelMultiplier;
|
||||||
|
|
||||||
|
return {
|
||||||
|
kCache: {
|
||||||
|
size: kCacheSize,
|
||||||
|
formatted: formatBytes(kCacheSize)
|
||||||
|
},
|
||||||
|
vCache: {
|
||||||
|
size: vCacheSize,
|
||||||
|
formatted: formatBytes(vCacheSize)
|
||||||
|
},
|
||||||
|
totalKVCache: {
|
||||||
|
size: totalKVCache,
|
||||||
|
formatted: formatBytes(totalKVCache)
|
||||||
|
},
|
||||||
|
totalMemory: totalMemory ? {
|
||||||
|
size: totalMemory,
|
||||||
|
formatted: formatBytes(totalMemory)
|
||||||
|
} : null,
|
||||||
|
effectiveLayers,
|
||||||
|
parallelMultiplier
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Validate input parameters
|
||||||
|
* @param {Object} params - Parameters to validate
|
||||||
|
* @param {string} lang - Language code ('ru' or 'en')
|
||||||
|
* @returns {Object} Validation result
|
||||||
|
*/
|
||||||
|
function validateParams(params, lang) {
|
||||||
|
lang = lang || 'ru';
|
||||||
|
const errors = [];
|
||||||
|
|
||||||
|
if (!params.contextLength || params.contextLength <= 0) {
|
||||||
|
if (lang === 'ru') {
|
||||||
|
errors.push('Длина контекста должна быть положительным числом');
|
||||||
|
} else {
|
||||||
|
errors.push('Context length must be a positive number');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.kvHeads || params.kvHeads <= 0) {
|
||||||
|
if (lang === 'ru') {
|
||||||
|
errors.push('Количество KV головок должно быть положительным числом');
|
||||||
|
} else {
|
||||||
|
errors.push('KV heads must be a positive number');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.headSize || params.headSize <= 0) {
|
||||||
|
if (lang === 'ru') {
|
||||||
|
errors.push('Размер головы должен быть положительным числом');
|
||||||
|
} else {
|
||||||
|
errors.push('Head size must be a positive number');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!params.numLayers || params.numLayers <= 0) {
|
||||||
|
if (lang === 'ru') {
|
||||||
|
errors.push('Количество слоев должно быть положительным числом');
|
||||||
|
} else {
|
||||||
|
errors.push('Number of layers must be a positive number');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.fullAttentionInterval && params.fullAttentionInterval <= 0) {
|
||||||
|
if (lang === 'ru') {
|
||||||
|
errors.push('Интервал полного внимания должен быть положительным числом');
|
||||||
|
} else {
|
||||||
|
errors.push('Full attention interval must be a positive number');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.modelSizeGB && params.modelSizeGB <= 0) {
|
||||||
|
if (lang === 'ru') {
|
||||||
|
errors.push('Размер модели должен быть положительным числом');
|
||||||
|
} else {
|
||||||
|
errors.push('Model size must be a positive number');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
valid: errors.length === 0,
|
||||||
|
errors
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate example parameters
|
||||||
|
* @returns {Object} Example parameters object
|
||||||
|
*/
|
||||||
|
function getExampleParams() {
|
||||||
|
return {
|
||||||
|
contextLength: 8192,
|
||||||
|
kType: 'f16',
|
||||||
|
vType: 'f16',
|
||||||
|
kvHeads: 32,
|
||||||
|
headSize: 128,
|
||||||
|
numLayers: 32,
|
||||||
|
modelSizeGB: 7,
|
||||||
|
parallel: 1,
|
||||||
|
fullAttentionInterval: null
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export for use in other files
|
||||||
|
if (typeof module !== 'undefined' && module.exports) {
|
||||||
|
module.exports = {
|
||||||
|
getQuantizationSize,
|
||||||
|
formatBytes,
|
||||||
|
calculateMemory,
|
||||||
|
validateParams,
|
||||||
|
getExampleParams,
|
||||||
|
QUANT_SIZES
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global export for browser
|
||||||
|
window.calculateMemory = calculateMemory;
|
||||||
|
window.validateParams = validateParams;
|
||||||
Loading…
Add table
Add a link
Reference in a new issue