대조 가로축을 동시성(9%만 공개)에서 모델 크기(100% 공개)로 교체 — 남의 기록이 실제로 찍히도록
Browse files- index.html +83 -98
index.html
CHANGED
|
@@ -206,6 +206,7 @@ svg{display:block; max-width:100%; height:auto}
|
|
| 206 |
|
| 207 |
.board{display:grid;grid-template-columns:minmax(260px,340px) 1fr;gap:20px;margin-top:30px;align-items:start}
|
| 208 |
.bpanel .f2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
|
|
|
|
| 209 |
.bpanel select,.bpanel input{width:100%;padding:10px 12px;border:1px solid var(--line2);
|
| 210 |
background:var(--surface);color:var(--ink);font:inherit;font-size:14px;border-radius:0}
|
| 211 |
.bpanel select:focus,.bpanel input:focus{outline:2px solid var(--blue);outline-offset:-2px}
|
|
@@ -615,9 +616,11 @@ table.brank tr.mine td{font-weight:600}
|
|
| 615 |
<div class="panel bpanel">
|
| 616 |
<div class="field"><label for="bGpu">쓰시는 GPU</label>
|
| 617 |
<select id="bGpu"></select></div>
|
| 618 |
-
<div class="
|
| 619 |
<div class="field"><label for="bQ">양자화</label><select id="bQ"></select></div>
|
| 620 |
-
<div class="field"><label for="
|
|
|
|
|
|
|
| 621 |
<input id="bAct" type="number" value="3" min="0.1" step="0.1"></div>
|
| 622 |
</div>
|
| 623 |
<div class="f2">
|
|
@@ -662,7 +665,7 @@ table.brank tr.mine td{font-weight:600}
|
|
| 662 |
<div class="note" id="bNote"><p>GPU를 고르면 계산합니다.</p></div>
|
| 663 |
|
| 664 |
<div class="tw"><table class="brank">
|
| 665 |
-
<thead><tr><th>#</th><th>모델</th><th>잰 곳</th><th class="n">
|
| 666 |
<th class="n">GPU당 tok/s</th><th>등급</th></tr></thead>
|
| 667 |
<tbody id="bTop"></tbody>
|
| 668 |
</table></div>
|
|
@@ -879,77 +882,69 @@ ${msg}`);
|
|
| 879 |
$('sendMsg').textContent = '메일 창이 열립니다. 열리지 않으면 contact@quantid.ai 로 보내주십시오.';
|
| 880 |
});
|
| 881 |
|
| 882 |
-
/* ===== 09 · 대조 — 업계
|
| 883 |
(function(){
|
| 884 |
var B=null, $b=function(i){return document.getElementById(i)};
|
| 885 |
-
var W=560,H=400, L=58,R=14,T=18,Bm=44;
|
| 886 |
var FX=function(n){return n.toLocaleString('ko-KR')};
|
| 887 |
|
| 888 |
-
fetch('board.json').then(function(r){return r.json()}).then(function(d){
|
| 889 |
-
|
| 890 |
-
}).catch(function(e){
|
| 891 |
-
$b('bNote').innerHTML='<p>대조 자료를 불러오지 못했습니다.</p>';
|
| 892 |
-
});
|
| 893 |
|
| 894 |
function init(){
|
| 895 |
var gs=Object.keys(B.hw).filter(function(k){
|
| 896 |
-
return B.records.some(function(r){return r.g===k});
|
| 897 |
-
|
| 898 |
-
$b('bGpu').innerHTML='<option value="">— 고르십시오 —</option>'+gs.map(function(k){
|
| 899 |
var n=B.records.filter(function(r){return r.g===k}).length;
|
| 900 |
-
return '<option value="'+k+'">'+k+' ('+n+'건)</option>';
|
| 901 |
-
|
| 902 |
-
|
| 903 |
-
|
| 904 |
|
| 905 |
-
|
| 906 |
-
|
| 907 |
-
return '<option value="'+q+'"'+(q==='FP8'?' selected':'')+'>'+q+'</option>';
|
| 908 |
-
}).join('');
|
| 909 |
|
| 910 |
-
|
|
|
|
| 911 |
$b('bSrc').textContent='전력은 제조사가 적어둔 정격이라 실제 쓰는 양보다 넉넉하게 잡힙니다. CPU와 냉각은 안 들어갔습니다. '+
|
| 912 |
'클라우드 값은 '+(B.src.price_by||[]).join(' · ')+'가 '+B.src.as_of+'에 내건 정가고, 약정하면 이보다 쌉니다. '+
|
| 913 |
-
'
|
|
|
|
| 914 |
|
| 915 |
-
['bGpu','bQ','bAct','bCon','bGn','bMon','bOut','bKwh'].forEach(function(id){
|
| 916 |
-
$b(id)
|
| 917 |
-
|
| 918 |
-
});
|
| 919 |
calc();
|
| 920 |
}
|
| 921 |
|
| 922 |
-
/* 물리 상한
|
| 923 |
-
tok/s ≤ 동시성 × 대역폭 ÷ 활성바이트 */
|
| 924 |
function ceil(hw, actB, q, con){
|
| 925 |
if(!hw||!hw.bw||!actB) return null;
|
| 926 |
-
|
| 927 |
-
return con * (hw.bw*1e12) / bytes;
|
| 928 |
}
|
| 929 |
|
| 930 |
function calc(){
|
| 931 |
var g=$b('bGpu').value; if(!g||!B) return;
|
| 932 |
var hw=B.hw[g], q=$b('bQ').value,
|
| 933 |
-
|
| 934 |
-
|
| 935 |
-
|
| 936 |
-
kwh=+$b('bKwh').value||0;
|
| 937 |
|
| 938 |
var peers=B.records.filter(function(r){return r.g===g});
|
| 939 |
-
var cap
|
| 940 |
|
| 941 |
-
/*
|
| 942 |
-
var near=peers.filter(function(r){return r.
|
| 943 |
-
var basis
|
| 944 |
-
var
|
| 945 |
if(basis.length){
|
| 946 |
var a=basis.map(function(r){return r.pg}).sort(function(x,y){return x-y});
|
| 947 |
-
|
| 948 |
}
|
| 949 |
-
var
|
| 950 |
-
if(est
|
| 951 |
|
| 952 |
-
draw(peers,
|
| 953 |
|
| 954 |
if(!est){
|
| 955 |
$b('bNote').innerHTML='<p>이 GPU는 기록이 모자라서 추정하지 않겠습니다. 없는 숫자를 지어내는 것보다 낫습니다.</p>';
|
|
@@ -957,95 +952,85 @@ ${msg}`);
|
|
| 957 |
return;
|
| 958 |
}
|
| 959 |
|
| 960 |
-
/* 순위 — GPU당 처리량 기준 백분위 */
|
| 961 |
var better=peers.filter(function(r){return r.pg>est}).length;
|
| 962 |
-
|
| 963 |
-
$b('kRank').textContent='상위 '+pct+'%';
|
| 964 |
$b('kRankSub').textContent=g+' 기록 '+peers.length+'건 가운데';
|
| 965 |
|
| 966 |
-
|
| 967 |
-
var needTok = mon*out;
|
| 968 |
-
var perGpuMonth = est*3600*24*30.4;
|
| 969 |
-
var need=Math.max(1, Math.ceil(needTok/perGpuMonth));
|
| 970 |
$b('kGpu').textContent=FX(need)+'장';
|
| 971 |
$b('kGpuSub').textContent='한 달 '+FX(Math.round(needTok/1e6))+'백만 토큰을 넘기려면';
|
| 972 |
|
| 973 |
-
|
| 974 |
-
if(hw.w){
|
| 975 |
-
var kW=need*hw.w/1000, mWh=kW*730;
|
| 976 |
$b('kPow').textContent=FX(Math.round(mWh*kwh))+'원';
|
| 977 |
$b('kPowSub').textContent=FX(Math.round(mWh))+' kWh · '+kW.toFixed(1)+' kW';
|
| 978 |
} else { $b('kPow').textContent='—'; $b('kPowSub').textContent='이 GPU는 정격이 공개 안 됨'; }
|
| 979 |
|
| 980 |
-
|
| 981 |
-
if(hw.usd){
|
| 982 |
-
var usd=need*hw.usd*730;
|
| 983 |
$b('kUsd').textContent=FX(Math.round(usd*1380/1e4))+'만원';
|
| 984 |
$b('kUsdSub').textContent='$'+FX(Math.round(usd))+' · 환율 1,380원';
|
| 985 |
} else { $b('kUsd').textContent='—'; $b('kUsdSub').textContent='공개된 시간당 단가가 없음'; }
|
| 986 |
|
| 987 |
-
var
|
| 988 |
$b('bNote').innerHTML='<p>이 설정이면 <strong>GPU 한 장에 초당 '+FX(Math.round(est))+'토큰</strong>쯤 나옵니다. '+
|
| 989 |
-
|
| 990 |
-
'
|
|
|
|
|
|
|
| 991 |
'도입 검토하실 때 쓰시는 장비에서 직접 재 드립니다.'+
|
| 992 |
(cap?' 참고로 이 조합이 물리적으로 넘을 수 없는 선은 초당 '+FX(Math.round(cap))+'토큰입니다.':'')+'</p>';
|
| 993 |
|
| 994 |
-
|
| 995 |
-
|
| 996 |
-
|
| 997 |
-
|
| 998 |
-
|
| 999 |
-
|
| 1000 |
}
|
| 1001 |
|
| 1002 |
-
function draw(peers,
|
| 1003 |
-
var
|
| 1004 |
-
|
| 1005 |
-
|
|
|
|
|
|
|
| 1006 |
y0=Math.max(.5,Math.min.apply(null,ys)), y1=Math.max.apply(null,ys);
|
| 1007 |
x1=Math.max(x1,x0*2); y1=Math.max(y1,y0*2);
|
| 1008 |
-
var lx=function(v){return L+(Math.log10(Math.max(v,x0))-Math.log10(x0))/(Math.log10(x1)-Math.log10(x0))*(W-L-R)},
|
| 1009 |
-
ly=function(v){return H-Bm-(Math.log10(Math.max(v,y0))-Math.log10(y0))/(Math.log10(y1)-Math.log10(y0))*(H-T-Bm)};
|
| 1010 |
|
| 1011 |
-
var s='<g font-family="IBM Plex Sans KR, sans-serif">';
|
| 1012 |
-
|
| 1013 |
-
|
| 1014 |
-
var yy=ly(Math.pow(10,p));
|
| 1015 |
s+='<line x1="'+L+'" y1="'+yy+'" x2="'+(W-R)+'" y2="'+yy+'" stroke="#DCD6C9" stroke-dasharray="2 3"/>'+
|
| 1016 |
'<text x="'+(L-7)+'" y="'+(yy+4)+'" text-anchor="end" font-size="9.5" fill="#8A8F98">'+
|
| 1017 |
-
(
|
| 1018 |
}
|
| 1019 |
-
for(
|
| 1020 |
-
var xx=lx(Math.pow(10,
|
| 1021 |
-
s+='<text x="'+xx+'" y="'+(H-Bm+16)+'" text-anchor="middle" font-size="9.5" fill="#8A8F98">'+Math.pow(10,
|
| 1022 |
}
|
| 1023 |
s+='<line x1="'+L+'" y1="'+(H-Bm)+'" x2="'+(W-R)+'" y2="'+(H-Bm)+'" stroke="#C4BCAB"/>'+
|
| 1024 |
'<line x1="'+L+'" y1="'+T+'" x2="'+L+'" y2="'+(H-Bm)+'" stroke="#C4BCAB"/>'+
|
| 1025 |
-
'<text x="'+((W+L)/2)+'" y="'+(H-8)+'" text-anchor="middle" font-size="11" fill="#4A515C">
|
| 1026 |
-
'<text x="14" y="'+((H-Bm+T)/2)+'" text-anchor="middle" font-size="11" fill="#4A515C" transform="rotate(-90 14 '+((H-Bm+T)/2)+')">GPU당
|
| 1027 |
|
| 1028 |
-
/* 물리 상한선 */
|
| 1029 |
if(cap){
|
| 1030 |
-
var off
|
| 1031 |
-
s+='<line x1="'+L+'" y1="'+cy+'" x2="'+(W-R)+'" y2="'+cy+'" stroke="#B0432C" stroke-width="1.6" stroke-dasharray="6 4" opacity="'+(off?'.
|
| 1032 |
-
'<text x="'+(W-R-4)+'" y="'+(cy+(off?14:-6))+'" text-anchor="end" font-size="10" font-weight="700" fill="#B0432C">
|
| 1033 |
-
FX(Math.round(cap))+(off?' (축 밖)':'')+'</text>';
|
| 1034 |
}
|
| 1035 |
-
|
| 1036 |
-
|
| 1037 |
-
|
| 1038 |
-
|
| 1039 |
-
'" fill="'+(r.own?'#3963CF':'#B8B0A0')+'" opacity="'+(r.own?'.95':'.55')+'"><title>'+
|
| 1040 |
-
(r.m||'')+' · '+(r.by||'')+' · c'+r.c+' · '+FX(r.pg)+' tok/s/GPU · '+(r.gr||'')+'</title></circle>';
|
| 1041 |
});
|
| 1042 |
-
/* 귀사 */
|
| 1043 |
if(est){
|
| 1044 |
-
var px=lx(
|
| 1045 |
-
s+='<
|
| 1046 |
-
'<circle cx="'+px+'" cy="'+py+'" r="12" fill="#2A7A4F" opacity=".16"/>'+
|
| 1047 |
'<circle cx="'+px+'" cy="'+py+'" r="6" fill="#2A7A4F" stroke="#fff" stroke-width="2"/>'+
|
| 1048 |
-
'<text x="'+(px+14)+'" y="'+(py-
|
|
|
|
| 1049 |
}
|
| 1050 |
$b('plot').innerHTML=s+'</g>';
|
| 1051 |
}
|
|
|
|
| 206 |
|
| 207 |
.board{display:grid;grid-template-columns:minmax(260px,340px) 1fr;gap:20px;margin-top:30px;align-items:start}
|
| 208 |
.bpanel .f2{display:grid;grid-template-columns:1fr 1fr;gap:12px}
|
| 209 |
+
.bpanel .f3{display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px}
|
| 210 |
.bpanel select,.bpanel input{width:100%;padding:10px 12px;border:1px solid var(--line2);
|
| 211 |
background:var(--surface);color:var(--ink);font:inherit;font-size:14px;border-radius:0}
|
| 212 |
.bpanel select:focus,.bpanel input:focus{outline:2px solid var(--blue);outline-offset:-2px}
|
|
|
|
| 616 |
<div class="panel bpanel">
|
| 617 |
<div class="field"><label for="bGpu">쓰시는 GPU</label>
|
| 618 |
<select id="bGpu"></select></div>
|
| 619 |
+
<div class="f3">
|
| 620 |
<div class="field"><label for="bQ">양자화</label><select id="bQ"></select></div>
|
| 621 |
+
<div class="field"><label for="bTot">모델 크기 (십억)</label>
|
| 622 |
+
<input id="bTot" type="number" value="35" min="0.1" step="0.1"></div>
|
| 623 |
+
<div class="field"><label for="bAct">그중 활성 (십억)</label>
|
| 624 |
<input id="bAct" type="number" value="3" min="0.1" step="0.1"></div>
|
| 625 |
</div>
|
| 626 |
<div class="f2">
|
|
|
|
| 665 |
<div class="note" id="bNote"><p>GPU를 고르면 계산합니다.</p></div>
|
| 666 |
|
| 667 |
<div class="tw"><table class="brank">
|
| 668 |
+
<thead><tr><th>#</th><th>모델</th><th>잰 곳</th><th class="n">크기</th>
|
| 669 |
<th class="n">GPU당 tok/s</th><th>등급</th></tr></thead>
|
| 670 |
<tbody id="bTop"></tbody>
|
| 671 |
</table></div>
|
|
|
|
| 882 |
$('sendMsg').textContent = '메일 창이 열립니다. 열리지 않으면 contact@quantid.ai 로 보내주십시오.';
|
| 883 |
});
|
| 884 |
|
| 885 |
+
/* ===== 09 · 대조 — 업계 기록 위에 지금 설정 찍기 ===== */
|
| 886 |
(function(){
|
| 887 |
var B=null, $b=function(i){return document.getElementById(i)};
|
| 888 |
+
var W=560,H=400, L=58,R=14,T=18,Bm=44;
|
| 889 |
var FX=function(n){return n.toLocaleString('ko-KR')};
|
| 890 |
|
| 891 |
+
fetch('board.json').then(function(r){return r.json()}).then(function(d){B=d; init();})
|
| 892 |
+
.catch(function(){ $b('bNote').innerHTML='<p>대조 자료를 불러오지 못했습니다.</p>'; });
|
|
|
|
|
|
|
|
|
|
| 893 |
|
| 894 |
function init(){
|
| 895 |
var gs=Object.keys(B.hw).filter(function(k){
|
| 896 |
+
return B.records.some(function(r){return r.g===k}); }).sort();
|
| 897 |
+
$b('bGpu').innerHTML=gs.map(function(k){
|
|
|
|
| 898 |
var n=B.records.filter(function(r){return r.g===k}).length;
|
| 899 |
+
return '<option value="'+k+'">'+k+' ('+n+'건)</option>'; }).join('');
|
| 900 |
+
$b('bGpu').value=gs.slice().sort(function(a,b){
|
| 901 |
+
return B.records.filter(function(r){return r.g===b}).length
|
| 902 |
+
- B.records.filter(function(r){return r.g===a}).length; })[0];
|
| 903 |
|
| 904 |
+
$b('bQ').innerHTML=Object.keys(B.bpp).map(function(q){
|
| 905 |
+
return '<option value="'+q+'"'+(q==='FP8'?' selected':'')+'>'+q+'</option>'; }).join('');
|
|
|
|
|
|
|
| 906 |
|
| 907 |
+
var withC=B.records.filter(function(r){return r.c}).length;
|
| 908 |
+
$b('bMeta').textContent='· 기록 '+B.records.length+'건 · GPU '+Object.keys(B.hw).length+'종 · 기준일 '+B.updated;
|
| 909 |
$b('bSrc').textContent='전력은 제조사가 적어둔 정격이라 실제 쓰는 양보다 넉넉하게 잡힙니다. CPU와 냉각은 안 들어갔습니다. '+
|
| 910 |
'클라우드 값은 '+(B.src.price_by||[]).join(' · ')+'가 '+B.src.as_of+'에 내건 정가고, 약정하면 이보다 쌉니다. '+
|
| 911 |
+
'동시 사용자 수는 '+B.records.length+'건 가운데 '+withC+'건만 공개돼 있어(MLPerf는 배치 크기를 안 밝힙니다) '+
|
| 912 |
+
'가로축은 모두가 밝힌 모델 크기로 잡았습니다. 둘 다 견적이 아니라 서로 견주는 잣대로 봐주십시오.';
|
| 913 |
|
| 914 |
+
['bGpu','bQ','bTot','bAct','bCon','bGn','bMon','bOut','bKwh'].forEach(function(id){
|
| 915 |
+
var e=$b(id); if(!e) return;
|
| 916 |
+
e.addEventListener('input', calc); e.addEventListener('change', calc); });
|
|
|
|
| 917 |
calc();
|
| 918 |
}
|
| 919 |
|
| 920 |
+
/* 물리 상한 — 디코드 한 스텝은 활성 가중치를 한 번 읽는다 */
|
|
|
|
| 921 |
function ceil(hw, actB, q, con){
|
| 922 |
if(!hw||!hw.bw||!actB) return null;
|
| 923 |
+
return con*(hw.bw*1e12)/(actB*1e9*(B.bpp[q]||1));
|
|
|
|
| 924 |
}
|
| 925 |
|
| 926 |
function calc(){
|
| 927 |
var g=$b('bGpu').value; if(!g||!B) return;
|
| 928 |
var hw=B.hw[g], q=$b('bQ').value,
|
| 929 |
+
tot=+$b('bTot').value||1, act=+$b('bAct').value||1,
|
| 930 |
+
con=+$b('bCon').value||1, mon=+$b('bMon').value||1,
|
| 931 |
+
out=+$b('bOut').value||1, kwh=+$b('bKwh').value||0;
|
|
|
|
| 932 |
|
| 933 |
var peers=B.records.filter(function(r){return r.g===g});
|
| 934 |
+
var cap=ceil(hw, act, q, con);
|
| 935 |
|
| 936 |
+
/* 비슷한 크기 모델의 실측에 견준다 */
|
| 937 |
+
var near=peers.filter(function(r){return r.tb && r.tb>=tot/2.5 && r.tb<=tot*2.5});
|
| 938 |
+
var basis=near.length>=3?near:peers;
|
| 939 |
+
var est=null;
|
| 940 |
if(basis.length){
|
| 941 |
var a=basis.map(function(r){return r.pg}).sort(function(x,y){return x-y});
|
| 942 |
+
est=a[Math.floor(a.length/2)];
|
| 943 |
}
|
| 944 |
+
var cut=false;
|
| 945 |
+
if(est&&cap&&est>cap){ est=cap; cut=true; }
|
| 946 |
|
| 947 |
+
draw(peers, cap, tot, est);
|
| 948 |
|
| 949 |
if(!est){
|
| 950 |
$b('bNote').innerHTML='<p>이 GPU는 기록이 모자라서 추정하지 않겠습니다. 없는 숫자를 지어내는 것보다 낫습니다.</p>';
|
|
|
|
| 952 |
return;
|
| 953 |
}
|
| 954 |
|
|
|
|
| 955 |
var better=peers.filter(function(r){return r.pg>est}).length;
|
| 956 |
+
$b('kRank').textContent='상위 '+Math.max(1,Math.round((better+1)/peers.length*100))+'%';
|
|
|
|
| 957 |
$b('kRankSub').textContent=g+' 기록 '+peers.length+'건 가운데';
|
| 958 |
|
| 959 |
+
var needTok=mon*out, need=Math.max(1, Math.ceil(needTok/(est*3600*24*30.4)));
|
|
|
|
|
|
|
|
|
|
| 960 |
$b('kGpu').textContent=FX(need)+'장';
|
| 961 |
$b('kGpuSub').textContent='한 달 '+FX(Math.round(needTok/1e6))+'백만 토큰을 넘기려면';
|
| 962 |
|
| 963 |
+
if(hw.w){ var kW=need*hw.w/1000, mWh=kW*730;
|
|
|
|
|
|
|
| 964 |
$b('kPow').textContent=FX(Math.round(mWh*kwh))+'원';
|
| 965 |
$b('kPowSub').textContent=FX(Math.round(mWh))+' kWh · '+kW.toFixed(1)+' kW';
|
| 966 |
} else { $b('kPow').textContent='—'; $b('kPowSub').textContent='이 GPU는 정격이 공개 안 됨'; }
|
| 967 |
|
| 968 |
+
if(hw.usd){ var usd=need*hw.usd*730;
|
|
|
|
|
|
|
| 969 |
$b('kUsd').textContent=FX(Math.round(usd*1380/1e4))+'만원';
|
| 970 |
$b('kUsdSub').textContent='$'+FX(Math.round(usd))+' · 환율 1,380원';
|
| 971 |
} else { $b('kUsd').textContent='—'; $b('kUsdSub').textContent='공개된 시간당 단가가 없음'; }
|
| 972 |
|
| 973 |
+
var others=basis.filter(function(r){return !r.own}).length;
|
| 974 |
$b('bNote').innerHTML='<p>이 설정이면 <strong>GPU 한 장에 초당 '+FX(Math.round(est))+'토큰</strong>쯤 나옵니다. '+
|
| 975 |
+
tot+'B 안팎 모델을 '+g+'에서 돌린 기록 '+basis.length+'건'+
|
| 976 |
+
(others?'(남이 잰 것 '+others+'건 포함)':'')+'의 한가운데 값입니다'+
|
| 977 |
+
(cut?'. 원래 중앙값은 물리적으로 불가능한 수준이라 상한까지 낮췄습니다':'')+'. '+
|
| 978 |
+
'<b>직접 잰 게 아니라 남의 기록에 견줘 본 값</b>이라 모델이나 서버 설정이 다르면 달라집니다. '+
|
| 979 |
'도입 검토하실 때 쓰시는 장비에서 직접 재 드립니다.'+
|
| 980 |
(cap?' 참고로 이 조합이 물리적으로 넘을 수 없는 선은 초당 '+FX(Math.round(cap))+'토큰입니다.':'')+'</p>';
|
| 981 |
|
| 982 |
+
$b('bTop').innerHTML=peers.slice().sort(function(a,b){return b.pg-a.pg}).slice(0,8)
|
| 983 |
+
.map(function(r,i){
|
| 984 |
+
return '<tr'+(r.own?' class="mine"':'')+'><td>'+(i+1)+'</td><td>'+(r.m||'—')+
|
| 985 |
+
'</td><td>'+(r.by||'—')+'</td><td class="n">'+(r.tb?r.tb+'B':'—')+
|
| 986 |
+
'</td><td class="n">'+FX(r.pg)+'</td><td><span class="gr g-'+(r.gr||'')+'">'+(r.gr||'—')+'</span></td></tr>';
|
| 987 |
+
}).join('');
|
| 988 |
}
|
| 989 |
|
| 990 |
+
function draw(peers, cap, tot, est){
|
| 991 |
+
var pts=peers.filter(function(r){return r.tb});
|
| 992 |
+
var xs=pts.map(function(r){return r.tb}).concat([tot]),
|
| 993 |
+
ys=pts.map(function(r){return r.pg}).concat(est?[est]:[]);
|
| 994 |
+
if(!xs.length) { $b('plot').innerHTML=''; return; }
|
| 995 |
+
var x0=Math.max(.3,Math.min.apply(null,xs)), x1=Math.max.apply(null,xs),
|
| 996 |
y0=Math.max(.5,Math.min.apply(null,ys)), y1=Math.max.apply(null,ys);
|
| 997 |
x1=Math.max(x1,x0*2); y1=Math.max(y1,y0*2);
|
| 998 |
+
var lx=function(v){return L+(Math.log10(Math.min(Math.max(v,x0),x1))-Math.log10(x0))/(Math.log10(x1)-Math.log10(x0))*(W-L-R)},
|
| 999 |
+
ly=function(v){return H-Bm-(Math.log10(Math.min(Math.max(v,y0),y1))-Math.log10(y0))/(Math.log10(y1)-Math.log10(y0))*(H-T-Bm)};
|
| 1000 |
|
| 1001 |
+
var s='<g font-family="IBM Plex Sans KR, sans-serif">', p;
|
| 1002 |
+
for(p=Math.ceil(Math.log10(y0)); p<=Math.floor(Math.log10(y1)); p++){
|
| 1003 |
+
var yy=ly(Math.pow(10,p)), lab=Math.pow(10,p);
|
|
|
|
| 1004 |
s+='<line x1="'+L+'" y1="'+yy+'" x2="'+(W-R)+'" y2="'+yy+'" stroke="#DCD6C9" stroke-dasharray="2 3"/>'+
|
| 1005 |
'<text x="'+(L-7)+'" y="'+(yy+4)+'" text-anchor="end" font-size="9.5" fill="#8A8F98">'+
|
| 1006 |
+
(lab>=1000?(lab/1000)+'k':lab)+'</text>';
|
| 1007 |
}
|
| 1008 |
+
for(p=Math.ceil(Math.log10(x0)); p<=Math.floor(Math.log10(x1)); p++){
|
| 1009 |
+
var xx=lx(Math.pow(10,p));
|
| 1010 |
+
s+='<text x="'+xx+'" y="'+(H-Bm+16)+'" text-anchor="middle" font-size="9.5" fill="#8A8F98">'+Math.pow(10,p)+'B</text>';
|
| 1011 |
}
|
| 1012 |
s+='<line x1="'+L+'" y1="'+(H-Bm)+'" x2="'+(W-R)+'" y2="'+(H-Bm)+'" stroke="#C4BCAB"/>'+
|
| 1013 |
'<line x1="'+L+'" y1="'+T+'" x2="'+L+'" y2="'+(H-Bm)+'" stroke="#C4BCAB"/>'+
|
| 1014 |
+
'<text x="'+((W+L)/2)+'" y="'+(H-8)+'" text-anchor="middle" font-size="11" fill="#4A515C">모델 크기 · 총 파라미터 (로그)</text>'+
|
| 1015 |
+
'<text x="14" y="'+((H-Bm+T)/2)+'" text-anchor="middle" font-size="11" fill="#4A515C" transform="rotate(-90 14 '+((H-Bm+T)/2)+')">GPU 한 장당 초당 토큰 (로그)</text>';
|
| 1016 |
|
|
|
|
| 1017 |
if(cap){
|
| 1018 |
+
var off=cap>y1, cy=off?T+4:ly(cap);
|
| 1019 |
+
s+='<line x1="'+L+'" y1="'+cy+'" x2="'+(W-R)+'" y2="'+cy+'" stroke="#B0432C" stroke-width="1.6" stroke-dasharray="6 4" opacity="'+(off?'.4':'1')+'"/>'+
|
| 1020 |
+
'<text x="'+(W-R-4)+'" y="'+(cy+(off?14:-6))+'" text-anchor="end" font-size="10" font-weight="700" fill="#B0432C">'+
|
| 1021 |
+
'넘을 수 없는 선 '+FX(Math.round(cap))+(off?' (축 밖)':'')+'</text>';
|
| 1022 |
}
|
| 1023 |
+
pts.forEach(function(r){
|
| 1024 |
+
s+='<circle cx="'+lx(r.tb).toFixed(1)+'" cy="'+ly(r.pg).toFixed(1)+'" r="'+(r.own?4.6:3.2)+
|
| 1025 |
+
'" fill="'+(r.own?'#3963CF':'#B8B0A0')+'" opacity="'+(r.own?'.95':'.5')+'"><title>'+
|
| 1026 |
+
(r.m||'')+' · '+(r.by||'')+' · '+(r.tb||'?')+'B · '+FX(r.pg)+' tok/s/GPU · '+(r.gr||'')+'</title></circle>';
|
|
|
|
|
|
|
| 1027 |
});
|
|
|
|
| 1028 |
if(est){
|
| 1029 |
+
var px=lx(tot), py=ly(est);
|
| 1030 |
+
s+='<circle cx="'+px+'" cy="'+py+'" r="13" fill="#2A7A4F" opacity=".15"/>'+
|
|
|
|
| 1031 |
'<circle cx="'+px+'" cy="'+py+'" r="6" fill="#2A7A4F" stroke="#fff" stroke-width="2"/>'+
|
| 1032 |
+
'<text x="'+(px<W-90?px+14:px-14)+'" y="'+(py-11)+'" text-anchor="'+(px<W-90?'start':'end')+
|
| 1033 |
+
'" font-size="11.5" font-weight="700" fill="#2A7A4F">지금 설정</text>';
|
| 1034 |
}
|
| 1035 |
$b('plot').innerHTML=s+'</g>';
|
| 1036 |
}
|