- الرئيسية
- Documentation
- الأصلية
- الترحيل إلى pi-natives (N-API) — ملاحظات ميدانية
الترحيل إلى pi-natives (N-API) — ملاحظات ميدانية
هذا دليل عملي لنقل المسارات الحرجة إلى crates/pi-natives وربطها من خلال روابط JS. وُجد هذا الدليل لتجنب تكرار نفس الأخطاء.
متى يجب الترحيل
Section titled “متى يجب الترحيل”قم بالترحيل عندما تكون أي من هذه الحالات صحيحة:
- المسار الحرج يعمل في حلقات العرض، أو تحديثات واجهة المستخدم المتكررة، أو الدفعات الكبيرة.
- تخصيصات JS هي المهيمنة (تكرار السلاسل النصية، التراجع في التعبيرات النمطية، المصفوفات الكبيرة).
- لديك بالفعل خط أساس في JS ويمكنك قياس أداء كلا الإصدارين جنبًا إلى جنب.
- العمل مرتبط بوحدة المعالجة المركزية أو إدخال/إخراج حاجب يمكن تشغيله على مجموعة خيوط libuv.
- العمل هو إدخال/إخراج غير متزامن يمكن تشغيله على وقت تشغيل Tokio (مثل تنفيذ الصدفة).
تجنب الترحيلات التي تعتمد على حالة خاصة بـ JS فقط أو الاستيرادات الديناميكية. يجب أن تكون صادرات N-API صافية، بيانات داخلة/بيانات خارجة. العمل طويل الأمد يجب أن يمر عبر task::blocking (مرتبط بوحدة المعالجة المركزية/إدخال وإخراج حاجب) أو task::future (إدخال/إخراج غير متزامن) مع إمكانية الإلغاء.
تشريح تصدير أصلي
Section titled “تشريح تصدير أصلي”جانب Rust:
- التنفيذ يوجد في
crates/pi-natives/src/<module>.rs. إذا أضفت وحدة جديدة، سجّلها فيcrates/pi-natives/src/lib.rs. - صدّر باستخدام
#[napi]؛ يتم تحويل الصادرات بصيغة snake_case إلى camelCase تلقائيًا. استخدمjs_nameصريحًا فقط للأسماء المستعارة الحقيقية/الأسماء غير الافتراضية. استخدم#[napi(object)]للهياكل. - استخدم
task::blocking(tag, cancel_token, work)(انظرcrates/pi-natives/src/task.rs) للعمل المرتبط بوحدة المعالجة المركزية أو الحاجب. استخدمtask::future(env, tag, work)للعمل غير المتزامن الذي يحتاج Tokio (مثل جلسات الصدفة). مرّرCancelTokenعندما تعرضtimeoutMsأوAbortSignal.
جانب JS:
packages/natives/src/bindings.tsيحتوي على واجهةNativeBindingsالأساسية.packages/natives/src/<module>/types.tsيعرّف أنواع TS ويوسّعNativeBindingsعبر دمج التصريحات.packages/natives/src/native.tsيستورد كل ملف<module>/types.tsلتفعيل التصريحات.packages/natives/src/<module>/index.tsيغلّف ربطnativeمنpackages/natives/src/native.ts.packages/natives/src/native.tsيحمّل الإضافة وvalidateNativeيفرض الصادرات المطلوبة.packages/natives/src/index.tsيعيد تصدير المغلّف للمستدعين فيpackages/*.
قائمة مراجعة الترحيل
Section titled “قائمة مراجعة الترحيل”- أضف تنفيذ Rust
- ضع المنطق الأساسي في دالة Rust عادية.
- إذا كانت وحدة جديدة، أضفها إلى
crates/pi-natives/src/lib.rs. - اعرضها باستخدام
#[napi]حتى يبقى التحويل الافتراضي من snake_case إلى camelCase متسقًا. - حافظ على التوقيعات مملوكة وبسيطة:
String،Vec<String>،Uint8Array، أوEither<JsString, Uint8Array>للمدخلات الكبيرة من السلاسل النصية/البايتات. - للعمل المرتبط بوحدة المعالجة المركزية أو الحاجب، استخدم
task::blocking؛ للعمل غير المتزامن، استخدمtask::future. مرّرCancelTokenواستدعِheartbeat()داخل الحلقات الطويلة.
- اربط روابط JS
- أضف الأنواع وتوسيع
NativeBindingsفيpackages/natives/src/<module>/types.ts. - استورد
./<module>/typesفيpackages/natives/src/native.tsلتفعيل دمج التصريحات. - أضف مغلّفًا في
packages/natives/src/<module>/index.tsيستدعيnative. - أعد التصدير من
packages/natives/src/index.ts.
- حدّث التحقق من الأصلي
- أضف
checkFn("newExport")فيvalidateNative(packages/natives/src/native.ts).
- أضف اختبارات الأداء
- ضع اختبارات الأداء بجوار الحزمة المالكة (
packages/tui/bench،packages/natives/bench، أوpackages/coding-agent/bench). - اشمل خط الأساس في JS والنسخة الأصلية في نفس التشغيل.
- استخدم
Bun.nanoseconds()وعدد تكرارات ثابت. - حافظ على مدخلات اختبار الأداء صغيرة وواقعية (بيانات فعلية مشاهدة في المسار الحرج).
- ابنِ الملف الثنائي الأصلي
bun --cwd=packages/natives run build- استخدم
bun --cwd=packages/natives run buildواضبطPI_DEV=1إذا أردت تشخيصات المحمّل أثناء الاختبار.
- شغّل اختبار الأداء
bun run packages/<pkg>/bench/<bench>.ts(أوbun --cwd=packages/natives run bench)
- قرّر بشأن الاستخدام
- إذا كان الأصلي أبطأ، أبقِ على JS واترك التصدير الأصلي غير مستخدم.
- إذا كان الأصلي أسرع، بدّل مواقع الاستدعاء إلى المغلّف الأصلي.
نقاط الألم وكيفية تجنبها
Section titled “نقاط الألم وكيفية تجنبها”1) ملف pi_natives.node القديم يمنع الصادرات الجديدة
Section titled “1) ملف pi_natives.node القديم يمنع الصادرات الجديدة”يفضّل المحمّل الملف الثنائي الموسوم بالمنصة في packages/natives/native (pi_natives.<platform>-<arch>.node). PI_DEV=1 الآن يُفعّل تشخيصات المحمّل فقط؛ لم يعد يتحول إلى اسم ملف إضافة تطوير منفصل. يوجد أيضًا ملف احتياطي pi_natives.node. الملفات الثنائية المُجمّعة تُستخرج إلى ~/.xcsh/natives/<version>/pi_natives.<platform>-<arch>.node. إذا كان أي من هذه قديمًا، فلن تُحدَّث الصادرات.
الحل: احذف الملف القديم قبل إعادة البناء.
rm packages/natives/native/pi_natives.linux-x64.noderm packages/natives/native/pi_natives.nodebun --cwd=packages/natives run buildإذا كنت تشغّل ملفًا ثنائيًا مُجمّعًا، احذف مجلد الإضافة المُخزّن مؤقتًا:
rm -rf ~/.xcsh/natives/<version>ثم تحقق من وجود التصدير في الملف الثنائي:
bun -e 'const tag = `${process.platform}-${process.arch}`; const mod = require(`./packages/natives/native/pi_natives.${tag}.node`); console.log(Object.keys(mod).includes("newExport"));'2) أخطاء “الصادرات المفقودة” من validateNative
Section titled “2) أخطاء “الصادرات المفقودة” من validateNative”هذا جيد — يمنع عدم التطابق الصامت. عندما ترى هذا:
Native addon missing exports ... Missing: visibleWidthفهذا يعني أن ملفك الثنائي قديم، أو أن اسم تصدير Rust (أو الاسم المستعار الصريح عند استخدامه) لا يتطابق مع اسم JS، أو أن التصدير لم يُجمّع أبدًا. أصلح البناء وعدم تطابق التسمية، لا تُضعف التحقق.
3) عدم تطابق توقيع Rust
Section titled “3) عدم تطابق توقيع Rust”حافظ على البساطة والملكية. String، Vec<String>، وUint8Array تعمل. تجنب المراجع مثل &str في الصادرات العامة. إذا كنت تحتاج بيانات منظمة، غلّفها في هياكل #[napi(object)].
4) أخطاء اختبار الأداء
Section titled “4) أخطاء اختبار الأداء”- لا تقارن مدخلات أو تخصيصات مختلفة.
- حافظ على استخدام JS والأصلي لمصفوفات مدخلات متطابقة.
- شغّل كليهما في نفس ملف اختبار الأداء لتجنب الانحراف.
قالب اختبار الأداء
Section titled “قالب اختبار الأداء”const ITERATIONS = 2000;
function bench(name: string, fn: () => void): number { const start = Bun.nanoseconds(); for (let i = 0; i < ITERATIONS; i++) fn(); const elapsed = (Bun.nanoseconds() - start) / 1e6; console.log(`${name}: ${elapsed.toFixed(2)}ms total (${(elapsed / ITERATIONS).toFixed(6)}ms/op)`); return elapsed;}
bench("feature/js", () => { jsImpl(sample);});
bench("feature/native", () => { nativeImpl(sample);});قائمة مراجعة التحقق
Section titled “قائمة مراجعة التحقق”validateNativeينجح (لا صادرات مفقودة).NativeBindingsمُوسَّعة فيpackages/natives/src/<module>/types.tsوالمغلّف مُعاد تصديره فيpackages/natives/src/index.ts.Object.keys(require(...))يتضمن تصديرك الجديد.- أرقام اختبار الأداء مُسجّلة في طلب السحب/الملاحظات.
- موقع الاستدعاء مُحدَّث فقط إذا كان الأصلي أسرع أو مساويًا.
القاعدة العامة
Section titled “القاعدة العامة”- إذا كان الأصلي أبطأ، لا تبدّل. أبقِ على التصدير للعمل المستقبلي، لكن واجهة المستخدم الطرفية يجب أن تبقى على المسار الأسرع.
- إذا كان الأصلي أسرع، بدّل موقع الاستدعاء وأبقِ على اختبار الأداء في مكانه لاكتشاف التراجعات.