ui: add dark mode
Browse files- assets/images/moon.svg +4 -0
- assets/images/sun.svg +12 -0
- dist/assets/images/memorycoalescing.png +2 -2
- dist/assets/images/moon.svg +1 -0
- dist/assets/images/sun.svg +1 -0
- dist/index.html +0 -0
- dist/main.bundle.js +187 -0
- dist/main.bundle.js.map +0 -0
- src/index.html +11 -6
- src/index.js +225 -0
assets/images/moon.svg
ADDED
|
assets/images/sun.svg
ADDED
|
dist/assets/images/memorycoalescing.png
CHANGED
![]() |
Git LFS Details
|
![]() |
Git LFS Details
|
dist/assets/images/moon.svg
ADDED
|
dist/assets/images/sun.svg
ADDED
|
dist/index.html
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
dist/main.bundle.js
CHANGED
@@ -5662,12 +5662,199 @@ function postMessageToHFSpaces(elementId) {
|
|
5662 |
}
|
5663 |
|
5664 |
;// ./src/index.js
|
|
|
|
|
|
|
5665 |
// import { plotClusters } from './clusters'
|
5666 |
|
5667 |
|
5668 |
|
|
|
|
|
5669 |
document.addEventListener("DOMContentLoaded", function () {
|
5670 |
console.log("DOMContentLoaded");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5671 |
loadFragments();
|
5672 |
init_memory_plot();
|
5673 |
syncHFSpacesURLHash();
|
|
|
5662 |
}
|
5663 |
|
5664 |
;// ./src/index.js
|
5665 |
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = src_unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t["return"] || t["return"](); } finally { if (u) throw o; } } }; }
|
5666 |
+
function src_unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return src_arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? src_arrayLikeToArray(r, a) : void 0; } }
|
5667 |
+
function src_arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
5668 |
// import { plotClusters } from './clusters'
|
5669 |
|
5670 |
|
5671 |
|
5672 |
+
// Dark mode is now handled manually via a CSS class on <html> and injected styles
|
5673 |
+
|
5674 |
document.addEventListener("DOMContentLoaded", function () {
|
5675 |
console.log("DOMContentLoaded");
|
5676 |
+
|
5677 |
+
// Inject minimal styles for the theme toggle button
|
5678 |
+
var styleEl = document.createElement('style');
|
5679 |
+
styleEl.textContent = "\n .theme-toggle-btn{position:absolute;top:16px;left:16px;z-index:10000;display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:999px;background:rgba(255,255,255,0.9);backdrop-filter:saturate(150%) blur(6px);cursor:pointer;border:1px solid transparent;outline:none;box-shadow:none;-webkit-appearance:none;appearance:none;-webkit-tap-highlight-color:transparent}\n .theme-toggle-btn:hover{border-color:transparent;box-shadow:none}\n .theme-toggle-btn:focus,.theme-toggle-btn:focus-visible{outline:none;border-color:transparent;box-shadow:none}\n .theme-toggle-btn img{width:22px;height:22px;transition:filter .15s ease}\n .theme-toggle-btn.dark img{filter: brightness(0) invert(1)}\n @media (prefers-color-scheme: dark){.theme-toggle-btn{background:rgba(30,30,30,0.85);border-color:transparent;box-shadow:none}}\n ";
|
5680 |
+
document.head.appendChild(styleEl);
|
5681 |
+
|
5682 |
+
// Inject dark mode CSS (scoped via html.dark)
|
5683 |
+
var darkCSS = "\n html.dark{color-scheme:dark}\n html.dark body{background:#242525;color:#e5e7eb}\n html.dark a{color:#93c5fd}\n html.dark .figure-legend{color:#9ca3af}\n html.dark d-article,html.dark d-article *{color:white!important;}\n html.dark d-contents{background:#242525}\n html.dark d-contents nav a{color:#cbd5e1}\n html.dark d-contents nav a:hover{text-decoration:underline solid rgba(255,255,255,0.6)}\n html.dark .note-box{background:#111;border-left-color:#888}\n html.dark .note-box-title{color:#d1d5db}\n html.dark .note-box-content{color:#e5e7eb}\n html.dark .large-image-background{background:#242525}\n html.dark .boxed-image{background:#111;border-color:#262626;box-shadow:0 4px 6px rgba(0,0,0,.6)}\n html.dark #graph-all,html.dark #controls,html.dark .memory-block,html.dark .activation-memory,html.dark .gradient-memory{background:#111;border-color:#262626;box-shadow:0 4px 6px rgba(0,0,0,.6);color:#e5e7eb}\n html.dark label,html.dark .memory-title{color:#e5e7eb}\n html.dark .memory-value{color:#93c5fd}\n html.dark input,html.dark select,html.dark textarea{background:#0f0f0f;color:#e5e7eb;border:1px solid #333}\n html.dark input:hover,html.dark select:hover,html.dark textarea:hover{border-color:#60a5fa}\n html.dark input:focus,html.dark select:focus,html.dark textarea:focus{border-color:#3b82f6;box-shadow:0 0 0 2px rgba(59,130,246,0.35)}\n html.dark input[type=range]{background:#333}\n html.dark input[type=range]::-webkit-slider-thumb{background:#3b82f6}\n html.dark .plotly_caption{color:#9ca3af}\n html.dark .theme-toggle-btn{background:rgba(30,30,30,0.85);border-color:transparent}\n html.dark d-article img{background:white}\n html.dark summary {color:black !important;}\n html.dark .katex-container {color:white !important;}\n html.dark d-code {background: white!important;}\n /* Table borders in dark mode */\n html.dark table{border-color:#262626}\n html.dark th,html.dark td{border-color:#262626}\n html.dark thead tr,html.dark tbody tr{border-color:#262626}\n html.dark d-byline, html.dark d-article{border-top: 1px solid rgba(255, 255, 255, 0.5);}\n html.dark d-byline h3{color:white;}\n html.dark d-math *, html.dark span.katex{color:white !important;}\n html.dark d-appendix { color: white}\n html.dark h1, html.dark h2, html.dark h3, html.dark h4, html.dark h5, html.dark h6 { color: white}\n html.dark .l-body { background: white;}\n \n \n ";
|
5684 |
+
var darkStyleEl = document.createElement('style');
|
5685 |
+
darkStyleEl.id = 'darkmode-css';
|
5686 |
+
darkStyleEl.textContent = darkCSS;
|
5687 |
+
document.head.appendChild(darkStyleEl);
|
5688 |
+
|
5689 |
+
// Inject equivalent dark CSS into all ShadowRoots using :host-context(.dark)
|
5690 |
+
// This ensures styles also apply inside web components with Shadow DOM
|
5691 |
+
var shadowDarkCSS = darkCSS.replace(/html\.dark/g, ':host-context(.dark)');
|
5692 |
+
var injectDarkStylesIntoRoot = function injectDarkStylesIntoRoot(root) {
|
5693 |
+
// Only target ShadowRoots here
|
5694 |
+
if (!root || !(root instanceof ShadowRoot)) return;
|
5695 |
+
if (root.querySelector('style#darkmode-css-shadow')) return;
|
5696 |
+
var style = document.createElement('style');
|
5697 |
+
style.id = 'darkmode-css-shadow';
|
5698 |
+
style.textContent = shadowDarkCSS;
|
5699 |
+
root.appendChild(style);
|
5700 |
+
};
|
5701 |
+
|
5702 |
+
// Normalize inline SVGs: ensure viewBox and preserveAspectRatio for responsiveness
|
5703 |
+
var normalizeSvgElement = function normalizeSvgElement(svgEl) {
|
5704 |
+
try {
|
5705 |
+
if (!svgEl || svgEl.hasAttribute('viewBox')) return;
|
5706 |
+
var widthAttr = svgEl.getAttribute('width');
|
5707 |
+
var heightAttr = svgEl.getAttribute('height');
|
5708 |
+
if (!widthAttr || !heightAttr) return;
|
5709 |
+
var width = parseFloat(widthAttr);
|
5710 |
+
var height = parseFloat(heightAttr);
|
5711 |
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return;
|
5712 |
+
svgEl.setAttribute('viewBox', "0 0 ".concat(width, " ").concat(height));
|
5713 |
+
if (!svgEl.hasAttribute('preserveAspectRatio')) {
|
5714 |
+
svgEl.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
5715 |
+
}
|
5716 |
+
} catch (_) {
|
5717 |
+
// no-op
|
5718 |
+
}
|
5719 |
+
};
|
5720 |
+
var processRootForSVGs = function processRootForSVGs(root) {
|
5721 |
+
if (!root || typeof root.querySelectorAll !== 'function') return;
|
5722 |
+
var svgs = root.querySelectorAll('svg:not([viewBox])');
|
5723 |
+
svgs.forEach(function (svg) {
|
5724 |
+
return normalizeSvgElement(svg);
|
5725 |
+
});
|
5726 |
+
};
|
5727 |
+
var _scanNodeForShadowRoots = function scanNodeForShadowRoots(node) {
|
5728 |
+
if (!node) return;
|
5729 |
+
if (node.shadowRoot) {
|
5730 |
+
injectDarkStylesIntoRoot(node.shadowRoot);
|
5731 |
+
processRootForSVGs(node.shadowRoot);
|
5732 |
+
}
|
5733 |
+
// Traverse children
|
5734 |
+
if (node.childNodes && node.childNodes.length) {
|
5735 |
+
node.childNodes.forEach(function (child) {
|
5736 |
+
// Process SVGs in this subtree as well
|
5737 |
+
processRootForSVGs(child);
|
5738 |
+
_scanNodeForShadowRoots(child);
|
5739 |
+
});
|
5740 |
+
}
|
5741 |
+
};
|
5742 |
+
|
5743 |
+
// Intercept future shadow roots
|
5744 |
+
var originalAttachShadow = Element.prototype.attachShadow;
|
5745 |
+
Element.prototype.attachShadow = function (init) {
|
5746 |
+
var shadow = originalAttachShadow.call(this, init);
|
5747 |
+
try {
|
5748 |
+
injectDarkStylesIntoRoot(shadow);
|
5749 |
+
processRootForSVGs(shadow);
|
5750 |
+
} catch (e) {}
|
5751 |
+
return shadow;
|
5752 |
+
};
|
5753 |
+
|
5754 |
+
// Initial sweep for any existing shadow roots
|
5755 |
+
_scanNodeForShadowRoots(document.documentElement);
|
5756 |
+
// Initial pass for regular DOM SVGs
|
5757 |
+
processRootForSVGs(document);
|
5758 |
+
|
5759 |
+
// Observe DOM mutations to catch dynamically added components
|
5760 |
+
var mo = new MutationObserver(function (mutations) {
|
5761 |
+
var _iterator = _createForOfIteratorHelper(mutations),
|
5762 |
+
_step;
|
5763 |
+
try {
|
5764 |
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
5765 |
+
var m = _step.value;
|
5766 |
+
m.addedNodes && m.addedNodes.forEach(function (n) {
|
5767 |
+
_scanNodeForShadowRoots(n);
|
5768 |
+
processRootForSVGs(n);
|
5769 |
+
});
|
5770 |
+
}
|
5771 |
+
} catch (err) {
|
5772 |
+
_iterator.e(err);
|
5773 |
+
} finally {
|
5774 |
+
_iterator.f();
|
5775 |
+
}
|
5776 |
+
});
|
5777 |
+
mo.observe(document.documentElement, {
|
5778 |
+
childList: true,
|
5779 |
+
subtree: true
|
5780 |
+
});
|
5781 |
+
|
5782 |
+
// Create the toggle button
|
5783 |
+
var btn = document.createElement('button');
|
5784 |
+
btn.className = 'theme-toggle-btn';
|
5785 |
+
btn.setAttribute('type', 'button');
|
5786 |
+
btn.setAttribute('aria-label', 'Basculer le mode sombre');
|
5787 |
+
// Reuse icons declared in HTML and move them into the button
|
5788 |
+
var sunIcon = document.getElementById('sunIcon');
|
5789 |
+
var moonIcon = document.getElementById('moonIcon');
|
5790 |
+
if (sunIcon && moonIcon) {
|
5791 |
+
// Make sure they adopt button sizing
|
5792 |
+
sunIcon.style.display = 'none';
|
5793 |
+
sunIcon.style.width = '22px';
|
5794 |
+
sunIcon.style.height = '22px';
|
5795 |
+
moonIcon.style.display = 'none';
|
5796 |
+
moonIcon.style.width = '22px';
|
5797 |
+
moonIcon.style.height = '22px';
|
5798 |
+
btn.appendChild(sunIcon);
|
5799 |
+
btn.appendChild(moonIcon);
|
5800 |
+
}
|
5801 |
+
document.body.appendChild(btn);
|
5802 |
+
var setIcon = function setIcon(enabled) {
|
5803 |
+
// enabled = dark mode enabled -> show sun (to indicate turning off), hide moon
|
5804 |
+
sunIcon.style.display = enabled ? '' : 'none';
|
5805 |
+
moonIcon.style.display = enabled ? 'none' : '';
|
5806 |
+
btn.setAttribute('title', enabled ? 'Désactiver le mode sombre' : 'Activer le mode sombre');
|
5807 |
+
btn.setAttribute('aria-pressed', String(enabled));
|
5808 |
+
btn.classList.toggle('dark', enabled);
|
5809 |
+
};
|
5810 |
+
var setDark = function setDark(enabled) {
|
5811 |
+
document.documentElement.classList.toggle('dark', enabled);
|
5812 |
+
setIcon(enabled);
|
5813 |
+
};
|
5814 |
+
var THEME_KEY = 'theme';
|
5815 |
+
var savedTheme = null;
|
5816 |
+
try {
|
5817 |
+
savedTheme = localStorage.getItem(THEME_KEY);
|
5818 |
+
} catch (e) {}
|
5819 |
+
var media = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)');
|
5820 |
+
var prefersDark = media ? media.matches : false;
|
5821 |
+
// Initialisation: priorité à la préférence sauvegardée, sinon préférence système
|
5822 |
+
if (savedTheme === 'dark') {
|
5823 |
+
setDark(true);
|
5824 |
+
} else if (savedTheme === 'light') {
|
5825 |
+
setDark(false);
|
5826 |
+
} else {
|
5827 |
+
setDark(prefersDark);
|
5828 |
+
}
|
5829 |
+
|
5830 |
+
// Si l'utilisateur a déjà choisi manuellement, on ne suit plus la préférence système
|
5831 |
+
var manualOverride = savedTheme === 'dark' || savedTheme === 'light';
|
5832 |
+
|
5833 |
+
// React to system preference changes dynamically (no persistence)
|
5834 |
+
if (media && typeof media.addEventListener === 'function') {
|
5835 |
+
media.addEventListener('change', function (e) {
|
5836 |
+
if (!manualOverride) {
|
5837 |
+
setDark(e.matches);
|
5838 |
+
}
|
5839 |
+
});
|
5840 |
+
} else if (media && typeof media.addListener === 'function') {
|
5841 |
+
// Fallback for older browsers
|
5842 |
+
media.addListener(function (e) {
|
5843 |
+
if (!manualOverride) {
|
5844 |
+
setDark(e.matches);
|
5845 |
+
}
|
5846 |
+
});
|
5847 |
+
}
|
5848 |
+
|
5849 |
+
// Toggle handler — for réduire les glitches, attendre le next frame avant d'ajuster l'icône
|
5850 |
+
btn.addEventListener('click', function () {
|
5851 |
+
manualOverride = true;
|
5852 |
+
var next = !document.documentElement.classList.contains('dark');
|
5853 |
+
setDark(next);
|
5854 |
+
try {
|
5855 |
+
localStorage.setItem(THEME_KEY, next ? 'dark' : 'light');
|
5856 |
+
} catch (e) {}
|
5857 |
+
});
|
5858 |
loadFragments();
|
5859 |
init_memory_plot();
|
5860 |
syncHFSpacesURLHash();
|
dist/main.bundle.js.map
CHANGED
The diff for this file is too large to render.
See raw diff
|
|
src/index.html
CHANGED
@@ -58,6 +58,9 @@
|
|
58 |
</script>
|
59 |
</d-front-matter>
|
60 |
<d-title>
|
|
|
|
|
|
|
61 |
<h1 class="l-page" style="text-align: center;">The Ultra-Scale Playbook:<br>Training LLMs on GPU Clusters</h1>
|
62 |
<div id="title-plot" class="main-plot-container l-screen" style="overflow-x: hidden; width: 100%; text-align: center;">
|
63 |
<div style="display: flex; justify-content: center; position: relative;">
|
@@ -65,12 +68,14 @@
|
|
65 |
</div>
|
66 |
<p style="text-align: cekter; font-style: italic; margin-top: 10px; max-width: 900px; margin-left: auto; margin-right: auto;">We ran over 4,000 scaling experiments on up to 512 GPUs and measured throughput (size of markers) and GPU utilization (color of markers). Note that both are normalized per model size in this visualization.</p>
|
67 |
|
68 |
-
|
69 |
-
<button class="order-button" style="margin: 0 8px;" onclick="window.open('https://www.lulu.com/shop/nouamane-tazi-and-ferdinand-mom-and-haojun-zhao-and-phuc-nguyen/the-ultra-scale-playbook/paperback/product-45yk9dj.html?page=1&pageSize=4', '_blank')">Order Book</button>
|
70 |
-
<button class="order-button" style="margin: 0 8px;" onclick="window.open('https://huggingface.co/nanotron', '_blank')">Get PDF</button>
|
71 |
-
</div>
|
72 |
</div>
|
|
|
73 |
</d-title>
|
|
|
|
|
|
|
|
|
74 |
<d-byline></d-byline>
|
75 |
<d-article>
|
76 |
<d-contents>
|
@@ -101,7 +106,7 @@
|
|
101 |
<aside>Note that we're still missing pipeline parallelism in this widget. To be added as an exercise for the reader.</aside>
|
102 |
|
103 |
<div class="large-image-background-transparent">
|
104 |
-
<div
|
105 |
<div id="graph-all">
|
106 |
<div class="figure-legend">Memory usage breakdown</div>
|
107 |
<div id="graph"></div>
|
@@ -837,7 +842,7 @@
|
|
837 |
frame.style.width = frame.contentWindow.document.documentElement.scrollWidth + 'px';
|
838 |
});
|
839 |
</script> -->
|
840 |
-
|
841 |
|
842 |
|
843 |
<p>We've also seen that data parallelism starts to have some limiting communication overhead above a certain level of scaling. Do we have other options for these larger models or large batch sizes? We do have some solutions, thankfully - they involve either moving some tensors to the CPU or splitting the weights/gradients/optimizer states tensors across GPU devices.</p>
|
|
|
58 |
</script>
|
59 |
</d-front-matter>
|
60 |
<d-title>
|
61 |
+
<!-- Theme toggle icons (declared once in DOM for clarity; visibility controlled via JS) -->
|
62 |
+
<img id="sunIcon" src="assets/images/sun.svg" alt="Sun icon" style="display:none;width:22px;height:22px;color:inherit"/>
|
63 |
+
<img id="moonIcon" src="assets/images/moon.svg" alt="Moon icon" style="display:none;width:22px;height:22px;color:inherit"/>
|
64 |
<h1 class="l-page" style="text-align: center;">The Ultra-Scale Playbook:<br>Training LLMs on GPU Clusters</h1>
|
65 |
<div id="title-plot" class="main-plot-container l-screen" style="overflow-x: hidden; width: 100%; text-align: center;">
|
66 |
<div style="display: flex; justify-content: center; position: relative;">
|
|
|
68 |
</div>
|
69 |
<p style="text-align: cekter; font-style: italic; margin-top: 10px; max-width: 900px; margin-left: auto; margin-right: auto;">We ran over 4,000 scaling experiments on up to 512 GPUs and measured throughput (size of markers) and GPU utilization (color of markers). Note that both are normalized per model size in this visualization.</p>
|
70 |
|
71 |
+
|
|
|
|
|
|
|
72 |
</div>
|
73 |
+
|
74 |
</d-title>
|
75 |
+
<div class="order-button-container">
|
76 |
+
<button class="order-button" style="margin: 0 8px;" onclick="window.open('https://www.lulu.com/shop/nouamane-tazi-and-ferdinand-mom-and-haojun-zhao-and-phuc-nguyen/the-ultra-scale-playbook/paperback/product-45yk9dj.html?page=1&pageSize=4', '_blank')">Order Book</button>
|
77 |
+
<button class="order-button" style="margin: 0 8px;" onclick="window.open('https://huggingface.co/nanotron', '_blank')">Get PDF</button>
|
78 |
+
</div>
|
79 |
<d-byline></d-byline>
|
80 |
<d-article>
|
81 |
<d-contents>
|
|
|
106 |
<aside>Note that we're still missing pipeline parallelism in this widget. To be added as an exercise for the reader.</aside>
|
107 |
|
108 |
<div class="large-image-background-transparent">
|
109 |
+
<div id="graph-container">
|
110 |
<div id="graph-all">
|
111 |
<div class="figure-legend">Memory usage breakdown</div>
|
112 |
<div id="graph"></div>
|
|
|
842 |
frame.style.width = frame.contentWindow.document.documentElement.scrollWidth + 'px';
|
843 |
});
|
844 |
</script> -->
|
845 |
+
<p><img alt="dp_ourjourney_memoryusage.svg" src="/assets/images/dp_ourjourney_memoryusage.svg" /></p>
|
846 |
|
847 |
|
848 |
<p>We've also seen that data parallelism starts to have some limiting communication overhead above a certain level of scaling. Do we have other options for these larger models or large batch sizes? We do have some solutions, thankfully - they involve either moving some tensors to the CPU or splitting the weights/gradients/optimizer states tensors across GPU devices.</p>
|
src/index.js
CHANGED
@@ -2,9 +2,234 @@
|
|
2 |
import { init_memory_plot } from './memory'
|
3 |
import { loadFragments } from './fragmentLoader'
|
4 |
import { syncHFSpacesURLHash } from './syncHFSpacesURLHash'
|
|
|
5 |
|
6 |
document.addEventListener("DOMContentLoaded", () => {
|
7 |
console.log("DOMContentLoaded");
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
8 |
loadFragments();
|
9 |
init_memory_plot();
|
10 |
syncHFSpacesURLHash();
|
|
|
2 |
import { init_memory_plot } from './memory'
|
3 |
import { loadFragments } from './fragmentLoader'
|
4 |
import { syncHFSpacesURLHash } from './syncHFSpacesURLHash'
|
5 |
+
// Dark mode is now handled manually via a CSS class on <html> and injected styles
|
6 |
|
7 |
document.addEventListener("DOMContentLoaded", () => {
|
8 |
console.log("DOMContentLoaded");
|
9 |
+
|
10 |
+
// Inject minimal styles for the theme toggle button
|
11 |
+
const styleEl = document.createElement('style');
|
12 |
+
styleEl.textContent = `
|
13 |
+
.theme-toggle-btn{position:absolute;top:16px;left:16px;z-index:10000;display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border-radius:999px;background:rgba(255,255,255,0.9);backdrop-filter:saturate(150%) blur(6px);cursor:pointer;border:1px solid transparent;outline:none;box-shadow:none;-webkit-appearance:none;appearance:none;-webkit-tap-highlight-color:transparent}
|
14 |
+
.theme-toggle-btn:hover{border-color:transparent;box-shadow:none}
|
15 |
+
.theme-toggle-btn:focus,.theme-toggle-btn:focus-visible{outline:none;border-color:transparent;box-shadow:none}
|
16 |
+
.theme-toggle-btn img{width:22px;height:22px;transition:filter .15s ease}
|
17 |
+
.theme-toggle-btn.dark img{filter: brightness(0) invert(1)}
|
18 |
+
@media (prefers-color-scheme: dark){.theme-toggle-btn{background:rgba(30,30,30,0.85);border-color:transparent;box-shadow:none}}
|
19 |
+
`;
|
20 |
+
document.head.appendChild(styleEl);
|
21 |
+
|
22 |
+
// Inject dark mode CSS (scoped via html.dark)
|
23 |
+
const darkCSS = `
|
24 |
+
html.dark{color-scheme:dark}
|
25 |
+
html.dark body{background:#242525;color:#e5e7eb}
|
26 |
+
html.dark a{color:#93c5fd}
|
27 |
+
html.dark .figure-legend{color:#9ca3af}
|
28 |
+
html.dark d-article,html.dark d-article *{color:white!important;}
|
29 |
+
html.dark d-contents{background:#242525}
|
30 |
+
html.dark d-contents nav a{color:#cbd5e1}
|
31 |
+
html.dark d-contents nav a:hover{text-decoration:underline solid rgba(255,255,255,0.6)}
|
32 |
+
html.dark .note-box{background:#111;border-left-color:#888}
|
33 |
+
html.dark .note-box-title{color:#d1d5db}
|
34 |
+
html.dark .note-box-content{color:#e5e7eb}
|
35 |
+
html.dark .large-image-background{background:#242525}
|
36 |
+
html.dark .boxed-image{background:#111;border-color:#262626;box-shadow:0 4px 6px rgba(0,0,0,.6)}
|
37 |
+
html.dark #graph-all,html.dark #controls,html.dark .memory-block,html.dark .activation-memory,html.dark .gradient-memory{background:#111;border-color:#262626;box-shadow:0 4px 6px rgba(0,0,0,.6);color:#e5e7eb}
|
38 |
+
html.dark label,html.dark .memory-title{color:#e5e7eb}
|
39 |
+
html.dark .memory-value{color:#93c5fd}
|
40 |
+
html.dark input,html.dark select,html.dark textarea{background:#0f0f0f;color:#e5e7eb;border:1px solid #333}
|
41 |
+
html.dark input:hover,html.dark select:hover,html.dark textarea:hover{border-color:#60a5fa}
|
42 |
+
html.dark input:focus,html.dark select:focus,html.dark textarea:focus{border-color:#3b82f6;box-shadow:0 0 0 2px rgba(59,130,246,0.35)}
|
43 |
+
html.dark input[type=range]{background:#333}
|
44 |
+
html.dark input[type=range]::-webkit-slider-thumb{background:#3b82f6}
|
45 |
+
html.dark .plotly_caption{color:#9ca3af}
|
46 |
+
html.dark .theme-toggle-btn{background:rgba(30,30,30,0.85);border-color:transparent}
|
47 |
+
html.dark d-article img{background:white}
|
48 |
+
html.dark summary {color:black !important;}
|
49 |
+
html.dark .katex-container {color:white !important;}
|
50 |
+
html.dark d-code {background: white!important;}
|
51 |
+
/* Table borders in dark mode */
|
52 |
+
html.dark table{border-color:#262626}
|
53 |
+
html.dark th,html.dark td{border-color:#262626}
|
54 |
+
html.dark thead tr,html.dark tbody tr{border-color:#262626}
|
55 |
+
html.dark d-byline, html.dark d-article{border-top: 1px solid rgba(255, 255, 255, 0.5);}
|
56 |
+
html.dark d-byline h3{color:white;}
|
57 |
+
html.dark d-math *, html.dark span.katex{color:white !important;}
|
58 |
+
html.dark d-appendix { color: white}
|
59 |
+
html.dark h1, html.dark h2, html.dark h3, html.dark h4, html.dark h5, html.dark h6 { color: white}
|
60 |
+
html.dark .l-body { background: white;}
|
61 |
+
|
62 |
+
|
63 |
+
`;
|
64 |
+
const darkStyleEl = document.createElement('style');
|
65 |
+
darkStyleEl.id = 'darkmode-css';
|
66 |
+
darkStyleEl.textContent = darkCSS;
|
67 |
+
document.head.appendChild(darkStyleEl);
|
68 |
+
|
69 |
+
// Inject equivalent dark CSS into all ShadowRoots using :host-context(.dark)
|
70 |
+
// This ensures styles also apply inside web components with Shadow DOM
|
71 |
+
const shadowDarkCSS = darkCSS.replace(/html\.dark/g, ':host-context(.dark)');
|
72 |
+
|
73 |
+
const injectDarkStylesIntoRoot = (root) => {
|
74 |
+
// Only target ShadowRoots here
|
75 |
+
if (!root || !(root instanceof ShadowRoot)) return;
|
76 |
+
if (root.querySelector('style#darkmode-css-shadow')) return;
|
77 |
+
const style = document.createElement('style');
|
78 |
+
style.id = 'darkmode-css-shadow';
|
79 |
+
style.textContent = shadowDarkCSS;
|
80 |
+
root.appendChild(style);
|
81 |
+
};
|
82 |
+
|
83 |
+
// Normalize inline SVGs: ensure viewBox and preserveAspectRatio for responsiveness
|
84 |
+
const normalizeSvgElement = (svgEl) => {
|
85 |
+
try {
|
86 |
+
if (!svgEl || svgEl.hasAttribute('viewBox')) return;
|
87 |
+
const widthAttr = svgEl.getAttribute('width');
|
88 |
+
const heightAttr = svgEl.getAttribute('height');
|
89 |
+
if (!widthAttr || !heightAttr) return;
|
90 |
+
const width = parseFloat(widthAttr);
|
91 |
+
const height = parseFloat(heightAttr);
|
92 |
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) return;
|
93 |
+
svgEl.setAttribute('viewBox', `0 0 ${width} ${height}`);
|
94 |
+
if (!svgEl.hasAttribute('preserveAspectRatio')) {
|
95 |
+
svgEl.setAttribute('preserveAspectRatio', 'xMidYMid meet');
|
96 |
+
}
|
97 |
+
} catch (_) {
|
98 |
+
// no-op
|
99 |
+
}
|
100 |
+
};
|
101 |
+
|
102 |
+
const processRootForSVGs = (root) => {
|
103 |
+
if (!root || typeof root.querySelectorAll !== 'function') return;
|
104 |
+
const svgs = root.querySelectorAll('svg:not([viewBox])');
|
105 |
+
svgs.forEach((svg) => normalizeSvgElement(svg));
|
106 |
+
};
|
107 |
+
|
108 |
+
const scanNodeForShadowRoots = (node) => {
|
109 |
+
if (!node) return;
|
110 |
+
if (node.shadowRoot) {
|
111 |
+
injectDarkStylesIntoRoot(node.shadowRoot);
|
112 |
+
processRootForSVGs(node.shadowRoot);
|
113 |
+
}
|
114 |
+
// Traverse children
|
115 |
+
if (node.childNodes && node.childNodes.length) {
|
116 |
+
node.childNodes.forEach((child) => {
|
117 |
+
// Process SVGs in this subtree as well
|
118 |
+
processRootForSVGs(child);
|
119 |
+
scanNodeForShadowRoots(child);
|
120 |
+
});
|
121 |
+
}
|
122 |
+
};
|
123 |
+
|
124 |
+
// Intercept future shadow roots
|
125 |
+
const originalAttachShadow = Element.prototype.attachShadow;
|
126 |
+
Element.prototype.attachShadow = function(init) {
|
127 |
+
const shadow = originalAttachShadow.call(this, init);
|
128 |
+
try {
|
129 |
+
injectDarkStylesIntoRoot(shadow);
|
130 |
+
processRootForSVGs(shadow);
|
131 |
+
} catch (e) {}
|
132 |
+
return shadow;
|
133 |
+
};
|
134 |
+
|
135 |
+
// Initial sweep for any existing shadow roots
|
136 |
+
scanNodeForShadowRoots(document.documentElement);
|
137 |
+
// Initial pass for regular DOM SVGs
|
138 |
+
processRootForSVGs(document);
|
139 |
+
|
140 |
+
// Observe DOM mutations to catch dynamically added components
|
141 |
+
const mo = new MutationObserver((mutations) => {
|
142 |
+
for (const m of mutations) {
|
143 |
+
m.addedNodes && m.addedNodes.forEach((n) => {
|
144 |
+
scanNodeForShadowRoots(n);
|
145 |
+
processRootForSVGs(n);
|
146 |
+
});
|
147 |
+
}
|
148 |
+
});
|
149 |
+
mo.observe(document.documentElement, { childList: true, subtree: true });
|
150 |
+
|
151 |
+
// Create the toggle button
|
152 |
+
const btn = document.createElement('button');
|
153 |
+
btn.className = 'theme-toggle-btn';
|
154 |
+
btn.setAttribute('type', 'button');
|
155 |
+
btn.setAttribute('aria-label', 'Basculer le mode sombre');
|
156 |
+
// Reuse icons declared in HTML and move them into the button
|
157 |
+
const sunIcon = document.getElementById('sunIcon');
|
158 |
+
const moonIcon = document.getElementById('moonIcon');
|
159 |
+
|
160 |
+
if (sunIcon && moonIcon) {
|
161 |
+
// Make sure they adopt button sizing
|
162 |
+
sunIcon.style.display = 'none';
|
163 |
+
sunIcon.style.width = '22px';
|
164 |
+
sunIcon.style.height = '22px';
|
165 |
+
moonIcon.style.display = 'none';
|
166 |
+
moonIcon.style.width = '22px';
|
167 |
+
moonIcon.style.height = '22px';
|
168 |
+
btn.appendChild(sunIcon);
|
169 |
+
btn.appendChild(moonIcon);
|
170 |
+
}
|
171 |
+
document.body.appendChild(btn);
|
172 |
+
|
173 |
+
const setIcon = (enabled) => {
|
174 |
+
// enabled = dark mode enabled -> show sun (to indicate turning off), hide moon
|
175 |
+
sunIcon.style.display = enabled ? '' : 'none';
|
176 |
+
moonIcon.style.display = enabled ? 'none' : '';
|
177 |
+
btn.setAttribute('title', enabled ? 'Désactiver le mode sombre' : 'Activer le mode sombre');
|
178 |
+
btn.setAttribute('aria-pressed', String(enabled));
|
179 |
+
btn.classList.toggle('dark', enabled);
|
180 |
+
};
|
181 |
+
|
182 |
+
const setDark = (enabled) => {
|
183 |
+
document.documentElement.classList.toggle('dark', enabled);
|
184 |
+
setIcon(enabled);
|
185 |
+
};
|
186 |
+
|
187 |
+
const THEME_KEY = 'theme';
|
188 |
+
let savedTheme = null;
|
189 |
+
try {
|
190 |
+
savedTheme = localStorage.getItem(THEME_KEY);
|
191 |
+
} catch (e) {}
|
192 |
+
|
193 |
+
const media = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)');
|
194 |
+
const prefersDark = media ? media.matches : false;
|
195 |
+
// Initialisation: priorité à la préférence sauvegardée, sinon préférence système
|
196 |
+
if (savedTheme === 'dark') {
|
197 |
+
setDark(true);
|
198 |
+
} else if (savedTheme === 'light') {
|
199 |
+
setDark(false);
|
200 |
+
} else {
|
201 |
+
setDark(prefersDark);
|
202 |
+
}
|
203 |
+
|
204 |
+
// Si l'utilisateur a déjà choisi manuellement, on ne suit plus la préférence système
|
205 |
+
let manualOverride = savedTheme === 'dark' || savedTheme === 'light';
|
206 |
+
|
207 |
+
// React to system preference changes dynamically (no persistence)
|
208 |
+
if (media && typeof media.addEventListener === 'function') {
|
209 |
+
media.addEventListener('change', (e) => {
|
210 |
+
if (!manualOverride) {
|
211 |
+
setDark(e.matches);
|
212 |
+
}
|
213 |
+
});
|
214 |
+
} else if (media && typeof media.addListener === 'function') {
|
215 |
+
// Fallback for older browsers
|
216 |
+
media.addListener((e) => {
|
217 |
+
if (!manualOverride) {
|
218 |
+
setDark(e.matches);
|
219 |
+
}
|
220 |
+
});
|
221 |
+
}
|
222 |
+
|
223 |
+
// Toggle handler — for réduire les glitches, attendre le next frame avant d'ajuster l'icône
|
224 |
+
btn.addEventListener('click', () => {
|
225 |
+
manualOverride = true;
|
226 |
+
const next = !document.documentElement.classList.contains('dark');
|
227 |
+
setDark(next);
|
228 |
+
try {
|
229 |
+
localStorage.setItem(THEME_KEY, next ? 'dark' : 'light');
|
230 |
+
} catch (e) {}
|
231 |
+
});
|
232 |
+
|
233 |
loadFragments();
|
234 |
init_memory_plot();
|
235 |
syncHFSpacesURLHash();
|