С приходом нейросетей теперь можно решать задачи гораздо проще, правда и задачи бы такой не стояло не будь нейросетей и llama.cpp, и huggingface. В общем смысл статьи в том чтобы попытаться математически вычислить теоретическую скорость генерации токенов в программе llama.cpp для моделей на hugginface, просто открыв карточку модели и нажав на закладку (букмарклет). Вот как это выглядит:

Подсветка скорости генерации и максимальный контекст
Подсветка скорости генерации и максимальный контекст

На самом деле этих калькуляторов в интернете довольно много, но почему-то у меня они показывают совсем не то что я вижу в реальности. Опять же, для меня было открытие что я могу на своей RTX3070 8Gb запускать MOE модели до 35 млрд параметров на довольно неплохой скорости, просто потому что все нейронки в чатах говорят что все, конец - 8 Гб не хватит. Я думаю что они остановились в своем развитии где-то год назад и все еще частенько вспоминают про модели llama или mistral, что сейчас уже не совсем актуально, по крайней мере для использования, может для научных изысканий эти модели будут лучше современных.

В общем-то и в этом калькуляторе нет никаких новых идей, он работает как и все остальные - вычисляет скорость исходя из пропускной способности видеопамяти пользователя. Но как выяснилось просто поделить одно число на другое (вес модели на пропускную способность видеокарты) не совсем правильно, когда я сказал что в реале у меня другая скорость, нейронки стали вводить поправочные коэффициент в виде эффективности видеокарты (60-80%). Я подозреваю что это на самом деле имеет место быть - каждая видео карта настроена по-своему, кто-то их разгоняет, кто-то, возможно, наоборот ограничивает чтобы увеличить ресурс. Если скорость генерации +- с натяжкой еще билась, то расчетные цифры по контексту нейронка занижала вообще в разы. Мне просто стало интересно почему так: она говорит что максимальный контекст 20 тысяч условно, а я реально смог выжать 70 тысяч ( при тестировании контекст полностью не забивал, но как минимум программа запустилась и шла генерация, потому что бывало и такое что программа запустилась, но как только шла генерация сразу out of memry).

В общем я попросил сделать калькулятор по моим цифрам и по этой модели цифры бились, но когда я взял другую модель, вбил ее данные - цифры снова стали не актуальными. Тогда я решил прогнать на своем компе несколько моделей, замерил скорость, замерил +- максимально возможный контекст, при котором llama.cpp не вылетает по нехватке памяти, добавил метаданные с карточек моделей на huggingface и попросил нейронку сделать калькулятор, чтобы он показывал цифры близкие мне. Вот таблица моих замеров:

Скрытый текст

модель

токенов

время, с

т/с

квант

контекст

вес модели

Qwen_Qwen3.5-4B-Q4_K_M.gguf

5079

57

89,11

q4

26000

3,0 ГБ (3 013 027 808 байт)

Qwen_Qwen3.5-4B-Q4_K_M.gguf

6864

77

89,14

q4

100000

3,0 ГБ (3 013 027 808 байт)

Qwen_Qwen3.5-4B-Q4_K_M.gguf

5271

55

95,84

q4

115000

3,0 ГБ (3 013 027 808 байт)

Qwen_Qwen3.5-4B-Q8_0.gguf

6096

88

69,27

q8

70000

4,6 ГБ (4 622 131 168 байт)

Qwen_Qwen3.5-9B-Q4_K_M.gguf

5570

100

55,7

q4

43000

6,2 ГБ (6 169 341 984 байта)

модель

токенов

время, с

т/с

квант

контекст

вес модели

Nanbeige_Nanbeige4.2-3B-Q4_K_M.gguf

13860

230

60,26

q4

26000

2,7 ГБ (2 684 023 968 байт)

Nanbeige_Nanbeige4.2-3B-Q8_0.gguf

16031

384

41,75

q8

18000

4,4 ГБ (4 434 787 488 байт)

После того как нейронка проанализировала данные из таблицы, она выяснила что эффективность видеокарты имела строго определенную задержку по времени на 1 токен (у меня оказалось 5мс), модель назвала его оверхед и внедрила в формулу скорости, итоговая страница калькулятора лежит вот тут: llamacpp Inference Speed Calculator. Интересно другое. Еще давно я обратил внимание на модель от Nanbeige - Nanbeige4.2-3B всего 3 млрд параметров, но "мои тесты" проходила довольно неплохо для 3 мрд параметров, но меня в этот раз смутило то что скорость генерации была меньше ожидаемой, а контекста хватает вообще впритирку. Выяснилось что модель дважды прогоняет по всем весам, и грубо говоря становится вдвойне больше. Этот нюанс оказалось что математически тоже легко считается, просто нейронка добавила поле num_loops, которое тоже присутствует в метаданных модели, и таким образом учитывает в финальной формуле.

Но вбивать параметры модели в калькулятор мне показалось как-то не круто, поэтому и решил сделать букмарклет - скрипт сам берет нужные метаданные (через /api/models/.../tree находим первый .gguf в репо, качаем первые 400 КБ (Range-запрос, не весь файл), парсим заголовок GGUF: block_count, head_count_kv, key_length, context_length, full_attention_interval (гибриды → KV-слоёв = слоёв/interval), num_loops (Nanbeige → ×2). Если не вышло (gated-репо, сеть) — покажет только t/s, а в консоли (F12) будет пустой {}. ), подставляет их в формулу и рисует нужные цифры. Важно только в начале подставить свои цифры в константы.

Скрытый текст

Константы (в начале, правь под себя)

Значение

Что это

BW

448

ПСП памяти, ГБ/с

OV

5

overhead, мс/токен

V

8

VRAM, ГБ

R

1.2

резерв (из твоих точек OOM)

KB

2

байт/значение KV: 2 = F16 (flash attention), 4 = F32

Здесь резерв - это сколько видеопамяти занимает система, условные цифры которые.

Код букмерклета - просто добавить в закладки:

javascript:(async()=>{const BW=448,OV=5,V=8,R=1.2,KB=2,E=document.getElementsByClassName('group/gguf');if(!E.length)return alert('Нет GGUF-квантов');let u;try{const p=location.pathname.split('/'),j=await(await fetch('/api/models/'+p[1]+'/'+p[2]+'/tree/main?recursive=true%27)).json(),f=j.find(x=>/\.gguf$/i.test(x.path||%27%27));if(f)u=%27/%27+p[1]+%27/%27+p[2]+%27/resolve/main/%27+encodeURI(f.path)}catch(e){}const M={};if(u)try{const r=await fetch(u,{headers:{Range:%27bytes=0-400000%27}});if(r.status!=206)throw 0;const b=await r.arrayBuffer(),d=new DataView(b),t=new TextDecoder(),G=a=>Number(d.getBigUint64(a,1)),U=a=>d.getUint32(a,1);if(U(0)!=0x46554747)throw 0;let o=24;const n=G(16),rs=()=>{const l=G(o);o+=8;const s=t.decode(new Uint8Array(b,o,l));o+=l;return s},rv=y=>{if(y==8)return rs();if(y==9){const e=U(o);o+=4;const c=G(o);o+=8;for(let i=0;i<c;i++)rv(e);return}const z=[1,1,2,2,4,4,4,1,0,0,8,8,8][y],p=o;o+=z;return y==0?b[p]:y==2?d.getUint16(p,1):y==4?U(p):y>9?G(p):0};for(let i=0;i<n;i++){const k=rs();if(k[0]==%27t%27&&M.block_count)break;const y=U(o);o+=4;const v=rv(y),s=k.split(%27.%27).pop();if(k.indexOf(%27linear%27)<0&&/^(block_count|head_count(_kv)?|key_length|embedding_length|context_length|full_attention_interval|num_loops)$/.test(s))M[s]=v}}catch(e){}const I=M.full_attention_interval,H=M.head_count_kv||M.head_count,D=M.key_length||(M.embedding_length&&M.head_count?M.embedding_length/M.head_count:0),P=M.num_loops||1,K=M.block_count&&H&&D?2*(I?Math.ceil(M.block_count/I):M.block_count)*H*D*KB:0;for(const el of E){if(el.dataset.sc)continue;el.innerHTML=el.innerHTML.replace(/[\d.]+\s*GB/g,m=>{const z=parseFloat(m),q=1000/(P*z*1000/BW+OV);let c=%27%27;if(K){let x=Math.floor((V-z-R)*2**30/K);if(M.context_length)x=Math.min(x,M.context_length);c=%27 | %27+(x>0?Math.round(x/1000)+%27k%27:%27OOM%27)}return m+%27 <b style="color:lime">(%27+Math.round(q)+%27 t/s%27+c+(z>V?%27 &gt;VRAM%27:%27%27)+%27)</b>%27});el.dataset.sc=1}console.log(M)})()

Этот букмарклет конечно же больше баловство, потому что не учитывает много нюансов - те же MOE модели он не предназначен считать, метаданные считывает не всегда корректно, скорее всего не все учитывает, мало того, тот же контекст можно запускать с квантованием и тогда цифры будут совсем другие, да и скорость то же самое. Вот один пользователь Хабра смог ускорить генерацию просто поиграв с флагами запуска с 39 до 75 ток/с, рекомендую. И кстати, поэтому я и решил написать статью, может быть можно сделать калькулятор который сможет рассчитывать влияние флагов на скорость и контекст? То есть вдруг кого-то эта идея сподвигнет написать нормальный букмарклет (или калькулятор) который сможет точнее предсказывать скорость генерации и размер контекста под пользовательское железо. Делитесь в комментах правильно ли считает этот калькулятор для вашего железа, такие же цифры по "эффективности" (оверхед 5 мс) или может я вообще просто подогнал результат по 3 модели и на другие не работает (скорее всего так и есть).

Комментарии (0)