TLDR

  • YaFF هو تنسيق سلكي مفتوح المصدر من Yandex لـ Protobuf — Apache 2.0، حاليًا C++، v0.1.0.
  • يبقى الملف .proto هو مصدر الحقيقة؛ يتغير تخطيط الذاكرة الفعلية فقط.
  • وفقًا لمعايير Yandex، يقرأ التخطيط المسطح البيانات الساخنة أسرع بنحو 3.8× من FlatBuffers، ضمن 1.2× من بنية C++ الأولية.
  • أربعة تخطيطات – ثابتة، ومسطحة، ومتفرقة، وديناميكية – تستبدل سرعة القراءة بمرونة المخطط؛ الديناميكية هي الافتراضية.
  • تعمل شركة YaFF في نظام التوصيات الإعلانية الخاص بها، حيث تبلغ عن توفير بنسبة 10-20% في وحدة المعالجة المركزية على نطاق الإنتاج.
  • يتم الاعتماد بشكل تدريجي: قم بإسقاطه في مسار واحد ساخن، مع تحويل Protobuf ثنائي الاتجاه عند الحواف.

لدى Yandex مصدر مفتوح YaFF (تنسيق مسطح آخر) ضمن Apache 2.0. إنها مكتبة تسلسل C++ عالية الأداء. يوفر YaFF تنسيق سلكي بدون نسخة لنظام Protobuf البيئي. يبقى ملف .proto هو المصدر الوحيد للحقيقة. يغير التنسيق فقط كيفية تخزين البيانات في الذاكرة. وهو يركز على أوقات التشغيل من جانب الخادم.

ما هو يا إف

YaFF ليس بديلاً عن Protobuf. إنه تنسيق سلكي بديل لرسائل Protobuf. يقوم نفس مخطط .proto بإنشاء واجهة برمجة تطبيقات C++ شبيهة بـ proto. لا تحتاج القراءات إلى خطوة تحليل، لذا تأتي الحقول مباشرة من المخزن المؤقت. لا يزال بإمكان التعليمات البرمجية الأقل حساسية للأداء تحليل تنسيق السلك مرة أخرى إلى رسائل Protobuf. هذا التحويل ثنائي الاتجاه هو ما يجعل اعتماد كل وحدة على حدة أمرًا واقعيًا. يمكنك تقديم YaFF في مسار واحد ساخن وترك الباقي على Protobuf.

المشكلة التي تستهدفها

يمكن أن يستهلك تحليل Protobuf نسبًا مكونة من رقمين من وحدة المعالجة المركزية في الواجهات الخلفية ذات التحميل العالي. على نطاق واسع، هذا يرسم خرائط لآلاف النوى المادية. خيار النسخة الصفرية الشائع هو FlatBuffers، وهو أيضًا من Google. لكن FlatBuffers ليس من ضمن Protobuf ويتطلب الحفاظ على مخطط منفصل وطبقة تحويل. غير متوافق لغويًا مع Protobuf. الترحيل يعني وجود مخططات مكررة، وقواعد مختلفة لتطور المخطط، ومحولات ميدانية مكتوبة بخط اليد. تستنتج العديد من الفرق أن التكلفة لا تستحق العناء. تهدف YaFF إلى سد هذه الفجوة: قراءة النسخة الصفرية مع الحفاظ على دلالات Protobuf.

كيف تعمل التخطيطات

يقرر التخطيط كيفية تخزين الرسالة في المخزن المؤقت. إنه يغير التمثيل المادي فقط، ويترك المخطط والواجهات التي تم إنشاؤها دون تغيير. تقدم YaFF أربعة تخطيطات. مُثَبَّت عبارة عن بنية بسيطة ومعبأة بدون رأس ومخطط مجمد. مستوي يضيف رأسًا ثنائي البايت ويدعم تطور المخطط. متناثر يعالج الحقول من خلال جدول التعريف، ويناسب المخططات المتفرقة. متحرك هو الإعداد الافتراضي ويحدد مسطح أو متفرق في وقت التشغيل. فهو يستخدم Flat عندما يسمح المخطط بذلك، ثم يتحول إلى Sparse عندما يكسر التطور المحاذاة المسطحة.

تَخطِيط الوصول للقراءة النفقات العامة لكل رسالة تطور المخطط الأفضل ل
مُثَبَّت 1 قراءة، 0 فروع 0 بايت المجمدة البدائيات الصغيرة المضمنة
مستوي 2 يقرأ، 1 فرع 2 بايت مقيد (حفظ النوع) بيانات ساخنة وكثيفة
متناثر 4 قراءات، 2 فرعين 6 بايت غير مقيد مخططات متفرقة، تطور حر
ديناميكي (افتراضي) مسطحة أو متفرقة في وقت التشغيل 2 أو 6 بايت غير مقيد منطق التطبيق العام

المعيار

تشحن Yandex مجموعة معايير قابلة للتكرار، تم إنشاؤها باستخدام google/benchmark في إصدار الإصدار. الأرقام أدناه هي متوسط ​​النانو ثانية لكل قراءة على AMD EPYC 7713 مع Clang 20.1.8. أقل أسرع. في الحالة الهرمية الساخنة، يقرأ التخطيط المسطح 9.79 نانوثانية. يحتاج FlatBuffers إلى 37.30 ns، ويحتاج Protobuf إلى 219.35 ns. خط الأساس الأساسي لبنية C++ الخام هو 8.14 ns. لذا فإن التخطيط المسطح يقرأ حوالي 3.8× أسرع من FlatBuffers هنا، وحوالي 22× أسرع من Protobuf. يبقى ضمن 1.2 × من البنية الخام.

شكل وقت القراءة (ns) التباطؤ مقابل البنية الخام
هيكل C++ الخام 8.14 1.0×
تخطيط YaFF المسطح 9.79 1.2×
YaFF تخطيط متفرق 21.23 2.6×
FlatBuffers 37.30 4.6×
بروتوبوف 219.35 26.9×
متوسط ​​ns لكل قراءة، تسلسل هرمي / ساخن / بدون تخزين مؤقت متسلسل. مصدر: https://yaff.tech/docs/en/benchmarks/access

ملاحظة: تعتمد الأرقام المطلقة على وحدة المعالجة المركزية والذاكرة المضيفة. من المتوقع أن تظل النسب بين التنسيقات ثابتة عبر الأجهزة.

تفاصيل الاسم المستعار للمترجم

يقرأ كل من FlatBuffers وYaFF الحقول عن طريق إعادة تفسير الذاكرة الأولية كنوع الهدف. هذا النوع من التلاعب يترك TBAA بدون حقائق قوية بما فيه الكفاية. لذا فإن تحليل الاسم المستعار لـ LLVM يعود إلى حكم MayAlias ​​المحافظ. لا يمكن للمترجم بعد ذلك إثبات أن عمليات الوصول المتكررة آمنة لإعادة الاستخدام. تؤدي كتابة root.intermediate().leaf().a() مرتين إلى إعادة سير الشجرة في كل مرة. يضيف YaFF تعليقات توضيحية في التعليمات البرمجية التي تم إنشاؤها والتي تخبر المترجم عندما تكون إعادة الاستخدام آمنة. غالبًا ما تساعد التعليقات التوضيحية للكود الذي تم إنشاؤه بواسطة YaFF المترجم على إعادة استخدام سلسلة الوصول، طالما لم يتم تعديل الذاكرة ذات الصلة بين القراءات. طالما لم يتم كتابة أي شيء إلى الذاكرة بين القراءات، يقوم YaFF بتخزين سلسلة الوصول مؤقتًا من تلقاء نفسه.

حيث يناسب: حالات الاستخدام

يستهدف YaFF الأنظمة التي يمكنك من خلالها التحكم في كل من المنتج والمستهلك. تعتبر الواجهات الخلفية للتوصية وخدمة الإعلانات هي الأكثر ملاءمة. وفقًا لـ Yandex، تعمل YaFF في نظام التوصيات الإعلانية الخاص بها، حيث تبلغ عن توفير بنسبة 10-20٪ في وحدة المعالجة المركزية على نطاق الإنتاج. تعد الفهارس المعينة للذاكرة مناسبة ثانية. يمكن للمضيف الاحتفاظ بعشرات الجيجابايت من البيانات المحلية. تنجو هذه الفهارس القادرة على استخدام mmap من إعادة تشغيل الخدمة دون إعادة التحليل. تشترك فهارس البحث ومخازن الميزات وخدمات الخلاصات في ملف التعريف الثقيل هذا. يستهدف التخطيط العمودي المخطط التحليلات وخطوط تعلم الآلة ذات الحقول المتكررة الكبيرة. يمكن أيضًا أن يكون YaFF أكثر إحكاما من FlatBuffers، مما يساعد على سلوك ذاكرة التخزين المؤقت.

نظرة على الكود

يعكس مسار القراءة Protobuf، مطروحًا منه خطوة التحليل.

#include "feed.pb.h"     // generated by protoc
#include "feed.yaff.h"   // generated by yaff_generate()

// 1. Serialize an existing Protobuf message into a YaFF buffer.
feed::FeedResponse proto = LoadFeedResponse();
const auto buffer = yaff::Serialize<protoyaff::feed::FeedResponse>(proto);

// 2. Read fields directly from the buffer. There is no parsing step.
const auto& response = yaff::ReadMessage<protoyaff::feed::FeedResponse>(buffer.Data());
for (const auto& item : response.items()) {
    std::string_view title  = item.title();
    std::string_view author = item.author().name();  // empty if author is unset
}

// 3. Convert back to Protobuf when a consumer needs the parsed message.
feed::FeedResponse restored;
response.ParseTo(restored);

يمكنك إضافة YaFF من خلال CMake (find_package) أو Conan. يقوم إنشاء الكود بتشغيل protobuf_generate() ثم yaff_generate(). تعيش أنواع YaFF التي تم إنشاؤها في مساحة الاسم protoyaff::. ترتبط معظم المشاريع فقط بـ yaff::core وyaff::proto.


موارد:

تحقق من مستودع جيثب والتوثيق.



اكتشاف المزيد من كحيل | أخبار التقنية

اشترك للحصول على أحدث التدوينات المرسلة إلى بريدك الإلكتروني.

شاركها.
اترك تعليقاً

اكتشاف المزيد من كحيل | أخبار التقنية

اشترك الآن للاستمرار في القراءة والحصول على حق الوصول إلى الأرشيف الكامل.

متابعة القراءة