التوثيق
تقرير الأداء
كلفة تطبيقات NSH مقيسة بالأرقام. لمعرفة كيف يحافظ NSH على سرعتها وكيف تقيس أداء تطبيقك، راجع الأداء.
#القياسات المرجعية
من القياسات المرجعية الخاصة بـ NSH (على Apple silicon ما لم يُذكر خلاف ذلك):
| السيناريو | الأرقام |
|---|---|
| تطبيق عدّاد، بلا واجهة (headless) (debug) | أول إطار ~0.5–0.65 ms (58 عملية / 49 عنصر تركيب)؛ نقرة واحدة ~0.15 ms |
| تطبيق عدّاد، Mac Catalyst (release) | أول إطار ~1.05–1.15 ms (منها نحو 0.7 ms لتخطيط 10 نصوص بواسطة CoreText لأول مرة)؛ نقرة واحدة ~0.12 ms |
| قائمة صور المعرض، بلا واجهة (headless) (debug) | أول إطار 653 عملية / 224 عقدة، ~2.7 ms |
| تمرير قائمة الصور، Catalyst (release) | إعادة ربط صف واحد أثناء التمرير البطيء ~0.4 ms؛ شاشة كاملة من ~32 صفًا عند تمرير سريع 0.9–1.6 ms |
| تمرير قائمة الصور، محاكي Android (arm64، release) | إعادة ربط صف واحد ~0.3–0.4 ms (JS ~60 µs) |
| بدء تشغيل المحرك (release) | ~75–155 µs؛ الواجهات العامة (fetch، URL، …) لا تكلّف شيئًا حتى أول استخدام، ثم ~90 µs |
| إعادة التحميل الفوري (Hot reload) | ~120 ms من حفظ الملف حتى تشغيل النسخة الجديدة؛ وتطبيق التحديث 0.2–0.5 ms |
#Linux (GTK 4)
قيست على حاسوب مكتبي يعمل بنظام Linux (x86_64، 24 خيطًا، معالج رسوميات NVIDIA)، بنسخ release، والمعرض في نافذة 1896 × 1030 على شاشة 120 Hz، وكل قراءة أُخذت تلقائيًا بواسطة examples/gallery/tools/perf.py (nsh test --target linux --fps، سطر واحد كل ثانية؛ المعالج CPU يخص العملية بأكملها، كنسبة مئوية من نواة واحدة). Frame p95 هو زمن الخيط الرئيسي لكل إطار مرسوم عند المئين 95: وإذا كان أقل من 8.3 ms فإن الإطار يتسع داخل تحديث الشاشة بمعدل 120 Hz.
#العروض المتحركة
| العرض | FPS | Frame p95 | CPU |
|---|---|---|---|
| Stocks: 22 خطًا مصغّرًا حيًّا، ومخطط خلال اليوم، ودفتر أوامر، وشريط صفقات | 117–120 | 5–6 ms | 25–29% (1200 × 800)، 28–32% (1896 × 1030) |
| Stocks، والنافذة تملأ شاشة 4K بمعدل 144 Hz وبمقياس 1.6× | 143–145 | 4.1–4.4 ms | 26–29% |
| Canvas: مقياس، وساعة، ومصفوفة نقاط | 120 | 2.6–4.4 ms | 5–10% |
| Animations: ثلاث حلقات أصلية، وسوابض (springs)، وتسلسلات | 120 | 2.0 ms | 12–13% |
#كل صفحة
كل صفحة من صفحات المعرض، عند فتحها وأثناء تمرير الاختبار لها، في نافذة 1200 × 800 على شاشة 144 Hz (الجهاز نفسه). يرسم NSH إطارًا فقط عندما يتغير شيء على الشاشة، لذا فإن FPS هنا هو المعدل أثناء حدوث حركة: إطار واحد لكل فترة وسيطة بين الإطارات المرسومة (perf.py الخاص بـ moving)؛ وعند 144 للشاشة، يأتي كل إطار من إطارات الحركة أو التمرير بعد تحديث واحد من الإطار السابق. أكثر من تحديث واحد هي نسبة الإطارات التي استغرقت أكثر من تحديث واحد (6.9 ms). يُشغَّل GIF وWebP في Images بمعدلات إطاراتها الخاصة (نحو 50 FPS هنا)، وكذلك الفيديو. صفحات Physics وOverlays وMenus قصيرة: ففي 1200 × 800 يصل التمرير إلى نهايتها خلال بضعة إطارات، لذلك تُقاس في نافذة 900 × 500 حيث يتوفر لها متسع للتمرير.
| الصفحة | مصدر الإطارات | FPS | Frame p95 | أكثر من تحديث واحد | CPU |
|---|---|---|---|---|---|
| Stocks | بيانات حية، حلقات أصلية | 144 | 4.1 ms | 0% | 26% |
| Stocks، تمرير | بيانات حية، تمرير | 144 | 4.1 ms | 0% | 25% |
| Photos، تمرير | تمرير | 144 | 2.8 ms | 2% | 9% |
| Lists، تمرير | تمرير | 143 | 1.7 ms | 0% | 7% |
| Scrolling، تمرير | تمرير | 144 | 1.6 ms | 1% | 4% |
| Navigation، تمرير | تمرير | 143 | 1.7 ms | 0% | 2% |
| Form، تمرير | تمرير | 144 | 1.3 ms | 0% | 4% |
| Resizable، تمرير | تمرير | 143 | 0.6 ms | 0% | 1% |
| Dock، تمرير | تمرير | 142 | 0.5 ms | 1% | 1% |
| Gestures | سوابض (springs)، حلقة نابضة | 144 | 1.6 ms | 0% | 1–5% |
| Physics، تمرير | تمرير (نافذة 900 × 500) | 144 | 1.6 ms | 1% | 1% |
| Animations | حلقات أصلية | 144 | 2.6 ms | 0–1% | 5% |
| Canvas، تمرير | تمرير، حلقات أصلية | 144 | 4.2 ms | 0% | 1–4% |
| Styles، تمرير | تمرير | 144 | 2.3 ms | 2% | 3% |
| Tailwind | أدوات animate-* المساعدة (utilities) | 144 | 1.7 ms | 0% | 3–8% |
| Text، تمرير | تمرير | 144 | 1.9 ms | 0% | 4% |
| Text View، تمرير | تمرير | 141 | 1.8 ms | 2% | 7% |
| Images، تمرير | تمرير، إطارات GIF وWebP | 143 | 2.9 ms | 2% | 8% |
| Components، تمرير | تمرير | 144 | 1.4 ms | 0% | 3% |
| Overlays، تمرير | تمرير (نافذة 900 × 500) | 144 | 1.7 ms | 0% | 2% |
| Menus، تمرير | تمرير (نافذة 900 × 500) | 144 | 1.9 ms | 0% | 2% |
| Controls | انتقالات | 144 | 0.9 ms | 0% | 1–4% |
| Web and video | إطارات الفيديو، الصفحة | 135–142 | 1.0 ms | 0% | 7–8% |
#الذاكرة
في حالة السكون، بعد 3 ثوانٍ من البدء (مشغّلات release، نافذة 480 × 640). Private هي الذاكرة التي تملكها العملية وحدها (USS)؛ أما resident فتضيف صفحات المكتبات المشتركة (GTK، وتعريف GPU)، التي يشاركها كل تطبيق GTK على الجهاز:
| التطبيق | المُصيِّر (Renderer) | Private (USS) | Resident (RSS) |
|---|---|---|---|
تطبيق Rust (nsh init --template rust) | GL (ngl) | ~49 MB | ~142 MB |
| تطبيق Rust | Cairo (GSK_RENDERER=cairo) | ~28 MB | ~95 MB |
عدّاد TSX (examples/counter) | GL | ~79 MB | ~166 MB |
| نافذة GTK 4 بتسمية واحدة، للمقارنة | Vulkan / Cairo | ~62 / ~40 MB | ~132 / ~109 MB |
إعادة زيارة صفحة لا تضيف ذاكرة؛ أما أول زيارة لصفحة فتزيد حجم العملية بمقدار ما تخزّنه عمدًا في الذاكرة المؤقتة (صور مفكوكة الترميز، بحد أقصى 128 MB) وبالمكتبات التي تُحمَّل عند أول استخدام (WebKitGTK لعروض الويب).
#حجم التطبيق
تطبيق hello world أنشأه nsh init بكل قالب، وتطبيقان يستخدمهما الناس، كما يبنيهما nsh package --target linux لمعمارية x86_64. المشغّل (runner) هو الملف التنفيذي الوحيد للتطبيق؛ والأرشيف هو .tar.gz الذي يُوزَّع به. تُبنى تطبيقات hello world مع تحسين وقت الربط (link-time optimization) وتُجرَّد من الرموز (CARGO_PROFILE_RELEASE_LTO=fat، CARGO_PROFILE_RELEASE_CODEGEN_UNITS=1، CARGO_PROFILE_RELEASE_STRIP=true):
| التطبيق | لغة الكتابة | المشغّل | الأرشيف |
|---|---|---|---|
Hello world (--template rust) | Rust | 16.5 MB | 7.5 MB |
Hello world (--template ts) | TSX، على QuickJS | 25.7 MB | 11.6 MB |
Hello world (--template mixed) | TSX وحزمة Rust (crate) | 25.8 MB | 11.7 MB |
| sysmon 0.1.3 | Rust | 20.9 MB بعد التجريد | 9.7 MB |
| Omasend 0.2.0 | Rust | 22.3 MB | 9.8 MB |
تحتوي مشغّلات TSX على بيئة QuickJS التشغيلية والوحدات الأصلية والمُصيِّر؛ أما شيفرة التطبيق نفسه فهي حزمته (bundle) الموجودة بجانب المشغّل، وحجمها أقل من 1 KB في تطبيق hello world.
ما الذي يصنع هذه الأرقام:
- المُصيِّر: مع تعريف NVIDIA يرسم NSH بمُصيِّر GL الخاص بـ GSK، وتكلفة إطاراته جزء يسير من تكلفة مُصيِّر Vulkan هناك (عرض Animations: ~1 ms من الرسم لكل إطار بدلًا من ~9.5 ms). ولا يزال
GSK_RENDERERهو من يختار. - تحويل Canvas إلى نقاط (rasters): يُحوَّل كل canvas إلى نقاط مرة واحدة في بلاطات بحجم 256 px محفوظة على GPU. وتعيد القيمة المتحركة رسم المنطقة التي تغطيها العمليات التي تقرؤها فقط، فوق البكسلات المحفوظة للجزء الثابت من الرسم؛ وأي canvas خارج مجال الرؤية ينتظر حتى يُمرَّر إلى العرض.
- مسارات تُحفظ جانبًا مرة واحدة:
fillRectوfillTextبعد مسار طويل يتركانه في مكانه حتى يُستخدم مجددًا (خط من 400 نقطة ثم 240 شريطًا: 0.63 ms).
#macOS (AppKit)
قيست على MacBook Pro (Apple M3، 24 GB) على شاشته المدمجة ProMotion بمعدل 120 Hz، بنسخ release، والمعرض في نافذة 1200 × 800، وكل قراءة أُخذت تلقائيًا بواسطة examples/gallery/tools/perf.py (nsh test --target macos --fps، سطر واحد كل ثانية؛ المعالج CPU يخص العملية بأكملها، كنسبة مئوية من نواة واحدة، وGPU حصتها من معالج الرسوميات). المراحل هي أزمنة الخيط الرئيسي لكل إطار: tick (الحركات الأصلية لرابط العرض display link) وcanvas (إعادة تشغيل الرسومات).
#العروض المتحركة
| العرض | FPS | Frame p95 | CPU | GPU |
|---|---|---|---|---|
| Stocks: 22 خطًا مصغّرًا حيًّا، ومخطط خلال اليوم، ودفتر أوامر، وشريط صفقات | 120 | 2.7–3.7 ms | 33–36% | 18–22% |
| Canvas: مقياس، وساعة، ومصفوفة نقاط | 119–120 | 0.6–0.7 ms | 4–7% | 0% |
| Animations: ثلاث حلقات أصلية، وسوابض (springs)، وتسلسلات | 119–120 | 0.3–1.6 ms | 5–8% | 0% |
في Stocks يبلغ زمن الخيط الرئيسي للإطار نحو 1.6 ms للـ ticks، منها ~0.45 ms لإعادة تشغيل الأجزاء المتحركة من canvases؛ أما بقية كل canvas فيرسمه Core Animation على خيوطه الخاصة.
#كل صفحة
كل صفحة من صفحات المعرض، عند فتحها وأثناء تمرير الاختبار لها، في نافذة 1200 × 800 نفسها. وكما في Linux، فإن FPS هو المعدل أثناء حدوث حركة (إطار واحد لكل فترة وسيطة بين الإطارات المرسومة)؛ وعند 120 للشاشة، يأتي كل إطار من إطارات الحركة أو التمرير بعد تحديث واحد من الإطار السابق. أكثر من تحديث واحد هي نسبة الإطارات التي استغرقت أكثر من تحديث واحد (8.3 ms). الصفحة الساكنة لا ترسم أي إطار وتستهلك 2–5% من CPU. يُشغَّل GIF وWebP في Images عبر Core Animation، والفيديو عبر AVFoundation، بمعدلات إطاراتها الخاصة.
| الصفحة | مصدر الإطارات | FPS | Frame p95 | أكثر من تحديث واحد | CPU |
|---|---|---|---|---|---|
| Stocks | بيانات حية، حلقات أصلية | 120 | 3.2 ms | 0% | 35% |
| Stocks، تمرير | بيانات حية، تمرير | 120 | 3.5 ms | 0% | 36% |
| AI Chat، تمرير | تمرير | 120 | 2.4 ms | 1% | 5% |
| Photos، تمرير | تمرير | 120 | 1.4 ms | 0% | 10% |
| Lists، تمرير | تمرير | 120 | 1.6 ms | 0% | 8% |
| Scrolling، تمرير | تمرير | 120 | 2.7 ms | 0% | 6% |
| Navigation، تمرير | تمرير | 120 | 0.2 ms | 0% | 4% |
| Form، تمرير | تمرير | 120 | 0.6 ms | 0% | 5% |
| Resizable، تمرير | تمرير | 120 | 0.3 ms | 0% | 4% |
| Dock، تمرير | تمرير | 120 | 0.3 ms | 1% | 5% |
| Gestures | سوابض (springs)، حلقة نابضة | 120 | 0.4 ms | 0% | 4% |
| Physics، تمرير | تمرير | 120 | 0.3 ms | 0% | 4% |
| Animations | حلقات أصلية | 120 | 0.3 ms | 0% | 5% |
| Canvas، تمرير | تمرير، حلقات أصلية | 120 | 1.2 ms | 0% | 7% |
| Styles، تمرير | تمرير، تحويل متمايل | 120 | 1.0 ms | 0% | 6% |
| Tailwind | أدوات animate-* المساعدة (utilities) | 120 | 0.5 ms | 0% | 4% |
| Text، تمرير | تمرير | 120 | 0.3 ms | 0% | 5% |
| Text View، تمرير | تمرير | 120 | 0.7 ms | 0% | 5% |
| Images، تمرير | تمرير | 120 | 0.4 ms | 0% | 5% |
| Components، تمرير | تمرير | 120 | 0.3 ms | 0% | 5% |
| Overlays، تمرير | تمرير | 120 | 0.3 ms | 0% | 4% |
| Menus، تمرير | تمرير | 120 | 0.3 ms | 0% | 5% |
| Controls | انتقالات | 120 | 0.3 ms | 0% | 4% |
| Web and video، تمرير | تمرير | 120 | 0.2 ms | 0% | 6% |
| System | requestAnimationFrame، مؤقّتات | 120 | 0.2 ms | 0% | 3% |
| Standard APIs، تمرير | تمرير | 120 | 0.3 ms | 0% | 5% |
| Storage، تمرير | تمرير | 120 | 0.3 ms | 0% | 5% |
#الذاكرة
في حالة السكون، بعد 3 ثوانٍ من البدء (مشغّلات release). Footprint هي الذاكرة التي يحاسب بها macOS العملية (ما يعرضه Activity Monitor)؛ أما resident فتضيف صفحات المكتبات المشتركة للنظام:
| التطبيق | النافذة | Footprint | Resident (RSS) |
|---|---|---|---|
تطبيق Rust (nsh init --template rust) | 1200 × 800 | ~19 MB | ~86 MB |
عدّاد TSX (examples/counter) | 1200 × 800 | ~20 MB | ~89 MB |
| نافذة AppKit بتسمية واحدة، للمقارنة | 480 × 640 | ~17 MB | ~80 MB |
خلال جولة المعرض تشغل العملية ما بين 84 و230 MB حتى صفحة Canvas، ثم ~350–360 MB ابتداءً من صفحة Text View، وذلك مع ما تخزّنه عمدًا في الذاكرة المؤقتة (صور مفكوكة الترميز، بحد أقصى 128 MB؛ ومستندات صفحة Text View) ومع الأطر (frameworks) التي تُحمَّل عند أول استخدام (WebKit لعروض الويب)؛ أما نقاط raster الخاصة بصفحة Canvas، التي تصل إلى ~310 MB عند ظهور الـ canvases الثلاثة على الشاشة، فتزول عند إغلاق الصفحة.
#حجم التطبيق
تطبيقات hello world التي ينشئها nsh init بكل قالب، كما يبنيها nsh package --target macos لمعالجات Apple
silicon (arm64)، مع تحسين وقت الربط (link-time optimization) وإزالة الرموز كما في Linux، إضافةً إلى التطبيقين المذكورين أعلاه كما يُشحنان لمعالجات Apple
silicon. الملف التنفيذي هو المشغّل (runner) داخل .app؛ والملف المضغوط هو .app بعد ضغطه (ditto -c -k):
| التطبيق | لغة الكتابة | الملف التنفيذي | مضغوط .app |
|---|---|---|---|
Hello world (--template rust) | Rust | 12.4 MB | 6.0 MB |
Hello world (--template ts) | TSX على QuickJS | 19.8 MB | 9.5 MB |
Hello world (--template mixed) | TSX وحزمة Rust (crate) | 19.8 MB | 9.5 MB |
| sysmon 0.1.3 | Rust | 21.6 MB | 8.4 MB (.tar.gz الخاص بإصداره) |
| Omasend 0.2.0 | Rust | 18.4 MB | 9.7 MB (.tar.gz الخاص بإصداره) |
ما الذي يحقق هذه الأرقام:
- إطارات للتغييرات فقط: يعمل display link أثناء تشغيل حركة أصلية (native animation) ويتوقف معها؛ وتتوقف الحركة مع صفحتها (حذف العرض الخاص بها ينهي إطاراتها المفتاحية، وتتوقف الحلقة في
cleanupالخاص بمكوّنها). - رسم الـ canvas حيث يكلّف أقل: الـ canvas الذي يُرسم كثيرًا (حركة، بيانات حية) يُرسم على خيوط Core
Animation (
drawsAsynchronously)، مما يُبقي الخيط الرئيسي حرًا؛ أما الذي يُعاد رسمه بين الحين والآخر فيُرسم فورًا، دون ذاكرة الرسوميات التي يحتفظ بها الرسم غير المتزامن. وتعيد القيمة المتحركة رسم المنطقة التي تغطيها العمليات التي تقرؤها فقط. - الاحتفاظ بأجزاء الـ canvas الثابتة: متى بقي الرسم دون تغيير، يُحفظ الجزء الطويل الذي يسبق أول عملية متحركة كطبقة (layer)؛ ويرسم كل إطار هذه الطبقة ويعيد تشغيل الجزء المتحرك المتبقي فقط، فورًا على الخيط الرئيسي (مقياس صفحة Canvas وساعتها ومصفوفة نقاطها: بلا عمل على GPU وبنصف الذاكرة).
- رسم المحتوى عند تغيّره: تُرسم النصوص والـ canvas في طبقات خاصة بها، وتُرسم من جديد عند تغيّر محتواها أو حجمها، لا عند تحريكها أو تمريرها.
#Windows (Win32, Direct2D)
القياسات على حاسوب مكتبي بنظام Windows 11 (Intel Core i7-13700KF، ذاكرة 32 GB، NVIDIA GeForce RTX 3060 Ti) بشاشة 4K وتردد 144 Hz
بتكبير 150%، وبنسخ release، ومعرض الأمثلة (gallery) في نافذة 1200 × 800 (1800 × 1200 بكسل)، وكل قراءة تُؤخذ تلقائيًا بواسطة
examples/gallery/tools/perf.py (nsh test --target windows --fps، سطر واحد كل ثانية؛ استهلاك CPU للعملية كاملة، بنسبة مئوية
من نواة واحدة). المراحل هي ما يستغرقه الخيط الرئيسي لكل إطار: paint (رسم Direct2D للنافذة) وضمنه
canvas (تسجيل الرسومات وإعادة تشغيلها).
#العروض المتحركة
| العرض | FPS | زمن الإطار p95 | الرسم / canvas لكل إطار | CPU |
|---|---|---|---|---|
| Stocks: 22 مخططًا مصغرًا حيًا، ومخطط خلال اليوم، ودفتر أوامر، وشريط صفقات | 144 | 3.7–4.3 ms | 1.4 / 0.13 ms | 36–43% |
| Canvas: مقياس، وساعة، ومصفوفة نقاط | 144 | 1.0–2.0 ms | 0.8–1.4 / 0.05 ms | 21–35% |
| Animations: ثلاث حلقات أصلية، وsprings، وتسلسلات | 144 | 1.0–3.6 ms | 0.9–1.9 ms | 20–41% |
ينفّذ Stocks من 16 إلى 17 عملية commit في الثانية، تستغرق كل منها حتى 2.1 ms في JS و1.7 ms في النواة على خيط المحرّك (p95).
#كل صفحة
كل صفحة من صفحات المعرض، عند فتحها وأثناء تمرير الاختبار لها، في نافذة 1200 × 800 نفسها. كما في Linux و macOS، FPS هو المعدّل أثناء وجود حركة (إطار واحد لكل فجوة وسيطة بين الإطارات المرسومة)؛ وعند 144 للشاشة، يأتي كل إطار من حركة أو تمرير بعد تحديث واحد من سابقه. تجاوز تحديثًا واحدًا هي نسبة الإطارات التي استغرقت أكثر من تحديث واحد (6.9 ms). الصفحة الساكنة لا ترسم أي إطار وتستهلك 0–2% من CPU. صور GIF وWebP في صفحة Images تعمل بمعدلات إطاراتها الخاصة (نحو 40 FPS هنا). تُقاس صفحات Physics وOverlays وMenus وStorage في نافذة 900 × 500، حيث تتسع للتمرير.
| الصفحة | مصدر الإطارات | FPS | زمن الإطار p95 | تجاوز تحديث واحد | CPU |
|---|---|---|---|---|---|
| Stocks | بيانات حية، حلقات أصلية | 144 | 3.9 ms | 0% | 36% |
| Stocks, scrolling | بيانات حية، تمرير | 144 | 4.3 ms | 0% | 43% |
| Photos, scrolling | تمرير | 143 | 2.1 ms | 0% | 9% |
| Lists, scrolling | تمرير | 144 | 2.0 ms | 1% | 19% |
| Scrolling, scrolling | تمرير | 143 | 2.1 ms | 0% | 7% |
| Navigation, scrolling | تمرير | 144 | 0.8 ms | 0% | 9% |
| Form, scrolling | تمرير | 144 | 0.8 ms | 0% | 12% |
| Resizable, scrolling | تمرير | 143 | 0.4 ms | 0% | 4% |
| Dock, scrolling | تمرير | 144 | 0.5 ms | 1% | 5% |
| Gestures | springs، حلقة نابضة | 144 | 1.8 ms | 0% | 36% |
| Physics, scrolling | تمرير (نافذة 900 × 500) | 144 | 0.8 ms | 0% | 4% |
| Animations | حلقات أصلية | 144 | 1.0 ms | 0% | 28% |
| Canvas, scrolling | تمرير، حلقات أصلية | 144 | 2.0 ms | 0% | 35% |
| Styles, scrolling | تمرير، تحويل متمايل | 144 | 2.1 ms | 0% | 39% |
| Tailwind | أدوات animate-* المساعدة | 144 | 0.6 ms | 0% | 19% |
| Text, scrolling | تمرير | 144 | 1.6 ms | 0% | 8% |
| Text View, scrolling | تمرير | 144 | 1.1 ms | 0% | 10% |
| Images, scrolling | تمرير، إطارات GIF وWebP | 143 | 4.8 ms | 2% | 23% |
| Components, scrolling | تمرير | 144 | 1.0 ms | 1% | 11% |
| Overlays, scrolling | تمرير (نافذة 900 × 500) | 144 | 0.6 ms | 0% | 1% |
| Menus, scrolling | تمرير (نافذة 900 × 500) | 144 | 0.6 ms | 0% | 2% |
| Controls | انتقالات | 117–144 | 1.5 ms | 0% | 22% |
| Web and video, scrolling | تمرير | 144 | 1.6 ms | 0% | 15% |
| System | requestAnimationFrame، مؤقتات | 144 | < 0.1 ms | 0% | 1% |
| Storage, scrolling | تمرير (نافذة 900 × 500) | 144 | 0.5 ms | 0% | 1% |
#الذاكرة
في حالة السكون، بعد 3 ثوانٍ من البدء (مشغّلات release، نافذة 1200 × 800). Private working set هي الذاكرة التي تستخدمها العملية وحدها (عمود Memory في Task Manager)؛ وprivate bytes ما حجزته منها؛ وworking set تضيف صفحات مكتبات النظام التي تتشاركها مع عمليات أخرى (Direct3D وDirect2D وDirectWrite وبرنامج تشغيل GPU):
| التطبيق | Private working set | Private bytes | Working set |
|---|---|---|---|
تطبيق Rust (nsh init --template rust) | ~14 MB | ~60 MB | ~53–71 MB |
عدّاد TSX (examples/counter) | ~15 MB | ~61 MB | ~56–71 MB |
خلال جولة المعرض يبلغ working set نحو 108–115 MB في Stocks، ونحو 230 MB من صفحة Canvas، ونحو 320–340 MB من صفحة Text View، ونحو 390–405 MB بعد أن تفتح Web and video عنصر WebView2 (الذي تعمل صفحاته في عمليات WebView2 الخاصة به)، مع ما تخزّنه مؤقتًا عن قصد (الصور المفكوكة، بحد أقصى 128 MB؛ ومستندات صفحة Text View).
#حجم التطبيق
تطبيقات hello world التي ينشئها nsh init بكل قالب، كما يبنيها nsh package --target windows لمعمارية x86_64
(مشغّلات Rust مع تحسين وقت الربط وإزالة الرموز، كما في Linux). الملف التنفيذي هو <Name>.exe التطبيق؛
والملف المضغوط هو .zip المكتوب بجانب مجلده:
| التطبيق | لغة الكتابة | الملف التنفيذي | الملف المضغوط |
|---|---|---|---|
Hello world (--template rust) | Rust | 15.0 MB | 6.7 MB |
Hello world (--template ts) | TSX على QuickJS | 28.6 MB | 12.4 MB |
Hello world (--template mixed) | TSX وحزمة Rust (crate) | 26.3 MB | 11.5 MB |
ما الذي يحقق هذه الأرقام:
- إطارات للتغييرات فقط، تُرسم حيث تغيّرت فقط: يعمل المؤقّت (ticker) المتزامن مع المُركِّب (compositor) أثناء تشغيل حركة أصلية أو انتقال أو دالة رد نداء للإطار. يرسم الإطار مناطق العروض التي تغيّرت (حيث كانت وحيث صارت)، فوق البكسلات التي تحتفظ بها النافذة، ولا يقدّم للمُركِّب إلا هذه المناطق (Stocks: نحو 34 عرضًا في الإطار).
- يسجّل الـ canvas جزأه المتحرك: يُسجَّل الرسم كقوائم أوامر Direct2D، متجهيًا وحادًا عند أي مقياس. وعندما تتغيّر قيمة متحركة، لا يُعاد تسجيل إلا العمليات ابتداءً من أول عملية تقرأ قيمة متحركة؛ أما ما يسبقها فيُحتفظ به (Stocks: نحو 26 ms من التسجيل في الثانية لـ 22 مخططًا مصغرًا نابضًا والمخطط الرئيسي).
- يُرسم ما يظهر فقط: تُستثنى من الإطار العروض الأبناء لعرض تمرير أو عرض قص (clipping) التي تقع خارج مجال الرؤية، ولا ترسم العروض التي لا محتوى خاصًا بها أكثر من خلفيتها وحدودها.
#iOS (UIKit)
القياسات في محاكي iOS (iPhone 18 Pro Max، على MacBook Pro المذكور أعلاه)، بنسخ release، وبالجولة نفسها
(perf.py --target ios: nsh test --target ios --fps، لمسات إصبع كما يتلقاها UIKit). يعرض المحاكي
بتردد 60 Hz؛ وعلى iPhone بشاشة ProMotion تعمل display links في NSH بتردد 120 Hz للشاشة (التطبيقات التي يبنيها
nsh package ومضيف التطوير تطلب ذلك عبر CADisableMinimumFrameDurationOnPhone). المراحل هي tick (الحركات
الأصلية) وdrain (تطبيق دفعات المحرّك) وscroll (ما يتبع عرض التمرير: القوائم، والخلايا اللاصقة،
والقيم المرتبطة).
| الصفحة | مصدر الإطارات | FPS | زمن الإطار p95 | CPU |
|---|---|---|---|---|
| Home, scrolling | تمرير | 60 | 0.1 ms | 6% |
| Stocks | بيانات حية، حلقات أصلية | 60 | 0.3 ms | 22% |
| Stocks, scrolling | بيانات حية، تمرير | 60 | 0.3 ms | 25% |
| Photos, scrolling | تمرير | 60 | 1.2 ms | 6% |
| Lists, scrolling | تمرير | 60 | 1.1 ms | 8% |
| Scrolling, scrolling | تمرير | 60 | 0.1 ms | 7% |
| Navigation, scrolling | تمرير | 60 | 0.2 ms | 7% |
| Form, scrolling | تمرير | 60 | 0.3 ms | 7% |
| Gestures | springs، حلقة نابضة | 60 | 0.3 ms | 3% |
| Animations | حلقات أصلية | 60 | 0.7 ms | 6% |
| Canvas | حلقات أصلية | 60 | 0.4 ms | 16% |
| Styles | تحويل متمايل | 60 | 0.4 ms | 4% |
| Tailwind, scrolling | تمرير | 60 | 0.2 ms | 7% |
| Text, scrolling | تمرير | 60 | 0.1 ms | 7% |
| Text View, scrolling | تمرير | 60 | 0.1 ms | 6% |
| Images, scrolling | تمرير | 60 | 0.1 ms | 5% |
| Components, scrolling | تمرير | 60 | 0.2 ms | 6% |
| Overlays, scrolling | تمرير | 60 | 0.1 ms | 6% |
| Menus, scrolling | تمرير | 60 | 0.2 ms | 7% |
| Controls, scrolling | تمرير، انتقالات | 60 | 0.2 ms | 5% |
| Web and video, scrolling | تمرير | 60 | 0.1 ms | 8% |
| System | requestAnimationFrame، مؤقتات | 60 | 0.3 ms | 4% |
| Standard APIs, scrolling | تمرير | 60 | 0.1 ms | 7% |
| Storage, scrolling | تمرير | 60 | 0.1 ms | 8% |
FPS هو المعدّل أثناء وجود حركة، كما سبق: عند 60 للمحاكي، يأتي كل إطار بعد تحديث واحد من سابقه. كل صفحة لا يتحرك فيها شيء لا ترسم أي إطار وتستهلك 1–2% من CPU في حالة السكون. تشغل العملية 40 MB في الصفحة الرئيسية للمعرض ونحو 180 MB بعد الجولة كاملة (صور مخزّنة مؤقتًا عن قصد، ومستندات صفحة Text View).
على iPhone 17 Pro (ProMotion، 120 Hz)، مع مضيف التطوير وعرض توضيحي واحد على الشاشة، ترسم الصفحات المتحركة في كل تحديث:
| الصفحة | مصدر الإطارات | FPS | زمن الإطار p95 | تجاوز تحديث واحد | CPU | الذاكرة |
|---|---|---|---|---|---|---|
| Stocks | بيانات حية، حلقات أصلية | 120 | 0.2–0.3 ms | 0% | 46–70% | 41–43 MB |
| Canvas | حلقات أصلية | 120 | 0.1 ms | 0% | 25–36% | 37–38 MB |
| Animations | حلقات أصلية | 120 | 0.2 ms | 0% | 3–11% | 37 MB |
ينفّذ Stocks 20 عملية commit في الثانية (نبضة كل 50 ms)، تستغرق كل منها 1.5 ms في JS و1.4 ms في النواة على خيط المحرّك (p95 هو 2.2 و2.0 ms)، ويكون هذا الخيط مشغولًا بنسبة 4–6% من الوقت.
#قياس تطبيق
لقياس تطبيق بالطريقة نفسها، شغّل اختباراته أو خطواته باستخدام --fps و-v على --target linux أو macos أو windows أو ios:
cargo build --release -p nsh-cli
python3 examples/gallery/tools/perf.py --target macos --only Stocks,Canvas # the gallery, page by page
echo 'await app.wait(5000)' | nsh -v test App.tsx --target linux --fps # any app