پرش به محتوای اصلی
پرش به محتوای مقاله

درون مکانیزم کشینگ جدید برای رفع گلوگاه‌های پردازشی CPU

·۳۰ شهریور ۱۴۰۵۱۰ دقیقه مطالعه۲ بازدید
نمودار مقایسه سرعت توکنایزرها در وظایف encode و decode با افزایش طول متن، نشان‌دهنده عملکرد بهتر نسخه‌های جدیدتر
نمودار مقایسه سرعت توکنایزرها در وظایف encode و decode با افزایش طول متن، نشان‌دهنده عملکرد بهتر نسخه‌های جدیدتر
اشتراک‌گذاری
واقعاً چه چیز جدید است؟

جایگزینی موتور Regex با دستورات SIMD (از طریق Bitcannon) و حذف تخصیص‌های مکرر حافظه در حلقه ادغام BPE، که منجر به جهش سرعت تا ۳۰ برابر شده است.

اگر از مدل‌های زبانی با پنجره‌های متنی بسیار بزرگ استفاده می‌کنید، احتمالاً متوجه شده‌اید که گاهی سرعت پردازش شما نه به قدرت GPU، بلکه به سرعت CPU در آماده‌سازی متن وابسته است. حالا با انتشار نسخه v1 کتابخانه Tokenizers توسط Hugging Face، این سد پردازشی در بسیاری از مدل‌ها تا ۳۰ برابر فرو ریخته است. این به‌روزرسانی تضمین می‌کند که واحدهای پردازش گرافیکی (GPU) دیگر در انتظار تبدیل متن خام به شناسه‌های عددی توسط CPU نمانند و در حالت بیکاری (Idle) باقی نمانند.

برای سال‌ها، توکن‌سازی (Tokenization) — شبیه به خرد کردن یک متن طولانی به تکه‌های کوچک‌تر برای اینکه مدل بتواند آن‌ها را هضم کند — بخش سبکی از خط لوله یادگیری ماشین بود. برای درک عمیق‌تر این فرآیند، می‌توان به تحلیل فنی توکن‌سازی به عنوان پل ارتباطی زبان انسان و منطق ماشین رجوع کرد که جزئیات تبدیل متن به شناسه‌های ریاضی را بررسی می‌کند. از نظر محاسباتی، این مرحله در مقایسه با مدل‌سازی‌های سنگینی که در سایر بخش‌های خط لوله رخ می‌داد، بسیار سبک به نظر می‌رسید. اما با سریع‌تر شدن مدل‌ها و مقیاس‌پذیری حجم داده‌ها به مجموعه‌های عظیم، توکن‌ساز به یک گلوگاه پنهان تبدیل شد. آموزش روی مجموعه‌داده‌های حجیم، پاسخ‌دهی به درخواست‌های هم‌زمان متعدد یا پردازش مکرر ورودی‌های طولانی می‌تواند فشار چنان زیادی به توکن‌ساز وارد کند که مدل را از داده «گرسنه» کند (Data Starvation).

تصور کنید یک خط تولید فوق‌سریع دارید که ربات نهایی آن با سرعت برق کار می‌کند، اما کارگری که قطعات را برایش می‌آورد بسیار کند است؛ این دقیقاً وضعیت بسیاری از جریان‌های کاری یادگیری ماشین پیش از نسخه v1 بود. با بهینه‌سازی این فرآیند «تغذیه»، Hugging Face در حال جابه‌جا کردن سقف عملکرد برای آموزش و سرویس‌دهی درخواست‌های هم‌زمان است. این بازطراحی گسترده با بهره‌گیری از اکوسیستم متن‌باز و ایده‌هایی از کتابخانه‌هایی چون gigatoken، tiktoken، kitoken، tokie، fastokens، wordchipper و ai-tokenizer صورت گرفته است. علاوه بر این، شرکت‌های IBM و NVIDIA و تیم ExecuTorch با ارائه وصله‌ها (Patches) و کمک در تست روی طیف وسیعی از سخت‌افزارها، به گسترش پشتیبانی از پلتفرم‌های مختلف کمک کردند.

تغییر در معماری

تیم توسعه ساختار کتابخانه را از یک تک‌بسته (Single Crate) به یک ساختار فضای کاری (Workspace) تغییر داد. این تغییر اجازه می‌دهد تا برنامه‌ها فقط بخش‌هایی را که واقعاً به آن‌ها نیاز دارند متصل (Link) کنند. به این ترتیب، کتابخانه به بخش‌های مجزا تقسیم شده است: tk-encode برای زمان اجرای مورد نیاز، و بخش‌های tk-serialize، tk-convert و tk-train که تنها زمانی متصل می‌شوند که برنامه به‌طور خاص به آن‌ها نیاز داشته باشد.

نمودار مقایسه سرعت توکنایزرها در وظایف encode/decode با افزایش طول متن

توکن‌سازی در نسخه v1 چگونه کار می‌کند؟

برای درک این جهش سرعت، باید به چهار مرحله اصلی خط لوله توکن‌سازی نگاه کنیم. نسخه v1 خروجی، API، واژگان (Vocabulary) و رتبه‌های ادغام (Merge Ranks) نسخه v0.23 را کاملاً حفظ کرده اما این مراحل را بهینه کرده است:

  • نرمال‌سازی (Normalization): اعمال عملیاتی مانند کوچک کردن حروف (Lowercasing) یا استانداردسازی یونیکد روی متن خام.
  • پیش-توکن‌سازی (Pre-tokenization): تقسیم متن به تکه‌های کوچک‌تر به نام پیش-توکن‌ها.
  • مدل (The Model): تبدیل هر پیش-توکن به توکن‌ها و نگاشت آن‌ها به شناسه‌های (ID) موجود در واژگان. بیشترین بهینه‌سازی‌های نسخه v1 در این مرحله رخ داده است.
  • پس-پردازش (Post-processing): افزودن توکن‌های خاصی که مدل انتظار دارد (که اکنون به عنوان مرحله STAGE_POST در خط لوله معرفی شده است).

این کتابخانه در برابر خانواده‌های مختلف توکن‌ساز، جامع باقی مانده است. در حالی که هشت مورد از ده خانواده مدل اندازه‌گیری شده از BPE (Byte Pair Encoding) استفاده می‌کنند، کتابخانه همچنان از WordPiece و Unigram نیز پشتیبانی می‌کند. در روش BPE، فرآیند از بایت‌های یک پیش-توکن شروع شده و به‌طور مکرر جفت‌های مجاور با بالاترین رتبه را با هم ادغام می‌کند تا زمانی که هیچ جفت رتبه‌بندی شده‌ای باقی نماند. این رتبه‌بندی در طول آموزش یاد گرفته شده و همراه با توکن‌ساز ارائه می‌شود تا تضمین شود که یک متن یکسان همیشه شناسه‌های یکسانی تولید کند. نکته حیاتی این است که یک ادغام هرگز از مرز یک پیش-توکن عبور نمی‌کند.

Bitcannon: جایگزینی Regex با SIMD

بسیاری از مدل‌های BPE از یک عبارت منظم (Regular Expression) برای تقسیم متن ورودی به پیش-توکن‌ها استفاده می‌کنند. از آنجا که ادغام‌ها فقط داخل یک پیش-توکن رخ می‌دهند و هرگز از مرزها عبور نمی‌کنند، این تقسیم اولیه تعیین می‌کند که بقیه خط لوله چه چیزی را ببیند. چون Regex یک پارامتر ثابت برای مدل است و در زمان اجرا تغییر نمی‌کند، نیازی نیست در هر بار رمزگذاری، یک موتور Regex عمومی آن را تفسیر کند.

نسخه v1 این بخش را با bitcannon جایگزین کرده است؛ یک تابع دست‌نویس که از دستورات SIMD (Single Instruction, Multiple Data) در CPUهای مدرن استفاده می‌کند تا یک عملیات را به‌طور هم‌زمان روی چندین بایت اجرا کند.

  • Bitcannon بایت‌های ورودی را به صورت جریان‌های موازی از بیت‌ها می‌بیند.
  • مرزها به‌جای اسکن تک‌تک کاراکترها، از طریق عملیات بولی روی کل ثبات‌ها (Registers) تعیین می‌شوند.
  • این سیستم در هر عملیات ثبات، ۶۴ بایت را پردازش می‌کند؛ تکنیکی مشابه آنچه در Parabix برای پردازش متن و simdjson برای JSON استفاده می‌شود.

این بهینه‌سازی به‌طور خاص به مدل‌های GPT-2، cl100k، o200k، Tekken و DeepSeek سود می‌رساند. مدل‌هایی که با این گرامرهای خاص سازگار نیستند، همچنان از مسیر Regex استفاده می‌کنند و به همین دلیل است که میزان افزایش سرعت در خانواده‌های مختلف مدل متفاوت است.

مکانیزم کشینگ کلمات

متون دنیای واقعی تکراری هستند. از آنجا که BPE برای یک پیش-توکن خاص همیشه شناسه‌های توکن یکسانی تولید می‌کند، نسخه v1 یک حافظه موقت (Memo) محلی برای هر رشته (Thread-local) معرفی کرده است که بایت‌های پیش-توکن را مستقیماً به شناسه‌های نهایی نگاشت می‌کند.

وقتی توکن‌ساز با کلمه‌ای مواجه می‌شود که قبلاً پردازش کرده است، کل فرآیند ادغام را نادیده گرفته و نتیجه را مستقیماً از کش می‌خواند. با افزایش حجم ورودی، تعداد کلمات منحصربه‌فرد معمولاً کندتر از تعداد کل کلمات رشد می‌کند، به این معنی که کلمات تکراری سهم بیشتری از ورودی را تشکیل می‌دهند.

کشینگ زمانی بیشترین اثر را دارد که ورودی حاوی پیش-توکن‌های تکراری زیادی باشد. در مقابل، ورودی‌هایی با تکرار بسیار کم ممکن است هزینه جستجو در کش را بپردازند بدون اینکه نتایج زیادی (Hit) دریافت کنند. کاربران می‌توانند نتایج مربوط به پیشوندهای مشترک را با استفاده از دستور tokbench measure prefix-sharing روی مجموعه داده agentic_swe و مقایسه موتور pipeline در برابر pipeline-no-cache و hf-tokenizers بازتولید کنند.

بازنویسی حلقه ادغام

حلقه ادغام BPE — جایی که جفت‌های مجاور با اولویت بالاتر به‌طور مکرر با هم ترکیب می‌شوند — پیش از این یک مرکز هزینه بزرگ بود. پیاده‌سازی قدیمی برای هر فراخوانی حافظه جدیدی تخصیص می‌داد و برای هر پیش-توکن یک صف اولویت (Priority Queue) جدید می‌ساخت.

نسخه v1 این تخصیص‌های مکرر را با استفاده از یک بافر موقت (Scratch Buffer) که توسط فراخواننده مدیریت می‌شود، حذف کرده است. مکانیزم جدید شامل موارد زیر است:

  • آرایه‌های تخت (Flat Arrays): نمادها در یک آرایه تخت ذخیره می‌شوند و نمادهای مجاور از طریق موقعیت‌هایشان به هم لینک می‌شوند که باعث می‌شود به‌روزرسانی‌ها در حین ادغام ارزان‌تر شوند.
  • مقایسه اعداد صحیح: جفت‌های کاندید در مقادیر ۶۴ بیتی واحد بسته‌بندی می‌شوند، به‌طوری که رتبه ادغام در بیت‌های ارشد قرار می‌گیرد. اکنون مقایسه دو کاندید به یک مقایسه ساده اعداد صحیح تبدیل شده است.
  • منطق بدون شاخه (Branchless Logic): وضعیت «عدم ادغام در اینجا» به عنوان بزرگ‌ترین مقدار ممکن نمایش داده می‌شود، که به حلقه اجازه می‌دهد بدون نیاز به دستورات شرطی (Branch)، ادغام بعدی را پیدا کند.
  • دسته‌بندی (Batching): سیستم اکنون دسته‌ای از پیش-توکن‌ها را در یک فراخوانی واحد مدل پردازش می‌کند، به‌جای اینکه برای هر پیش-توکن یک فراخوانی مجزا داشته باشد.

بنچمارک‌های عملکرد

برای تضمین سازگاری، Hugging Face از قوانین سخت‌گیرانه‌ای برای اندازه‌گیری‌های tokbench استفاده کرد. آن‌ها قوانین زیر را برای یکسان نگه داشتن مقایسه بین موتورها به کار بردند:

  • یک حلقه زمان‌بندی: هر موتور همان حلقه یکسان را بدون مسیرهای سریع (Fast paths) اختصاصی اجرا می‌کند.
  • حذف زمان بارگذاری: زمان بارگذاری واژگان به‌طور جداگانه اندازه‌گیری شده و هرگز داخل فراخوانی encode قرار نمی‌گیرد.
  • تأیید هش شناسه‌ها: هش FNV-1a روی شناسه‌های خروجی باید دقیقاً با خط پایه (Baseline) مطابقت داشته باشد.
  • فقط سلول‌های مشترک: میانه (Median)‌ها فقط روی سلول‌هایی محاسبه می‌شوند که هر موتور آن‌ها را اجرا و تأیید کرده است.
  • جاروب کامل در هر پردازش: هر تکرار در یک پردازش جدید شروع شده و تمام سلول‌ها را حفظ می‌کند.
  • تثبیت هسته‌های فیزیکی (Physical-core pinning): کارگرها روی هشت هسته فیزیکی مجزا تثبیت شده‌اند و هرگز از رشته‌های SMT خواهر-برادر استفاده نمی‌کنند.
  • شغل‌های مستقل: کارهای مجزا برای اندازه‌گیری تغییرات میزبان-به-میزبان (Host-to-host) استفاده شدند.

آن‌ها همچنین از اسناد متمایزی برای نتایج نهایی استفاده کردند تا مطمئن شوند کل مجموعه داده برای جای گرفتن در کش بیش از حد بزرگ است و از سوگیری «کش گرم» (Warm cache bias) جلوگیری شود. رمزگذاری مکرر یک سند، عملکرد را زمانی می‌سنجد که سند در کش باشد، در حالی که رمزگذاری جریانی از اسناد متمایز، عملکرد را روی ورودی‌های جدید می‌سنجد در حالی که اجازه می‌دهد پیش-توکن‌های قبلاً دیده شده در کش باقی بمانند.

طبق این نتایج، دستاوردهای عملکردی قابل توجه است. در یک Apple M4 Max، مسیر رمزگذاری برای t5-base حدود ۳ برابر و برای gpt2 تا ۳۰ برابر سریع‌تر از نسخه v0.23 است.

نمودار مقایسه سرعت توکنایزرها: اندازه‌گیری عملکرد encode، decode و مقیاس‌پذیری در نسخه ۱

مقیاس‌پذیری نیز کارآمدتر شده است. کتابخانه به ۷۶٪ مقیاس‌پذیری خطی در هشت کارگر دست یافته است. این امر به این دلیل ممکن شد که هر رشته اکنون بافر موقت و کش کلمات خود را از زیر-استخر (Sub-pool) اختصاصی خود دریافت می‌کند و نیاز به صف انتظار برای یک قفل واحد (#2365) حذف شده است.

پیاده‌سازی و API

نسخه RC (Release Candidate) فعلی، API، واژگان و رتبه‌های ادغام موجود را حفظ کرده است. این نسخه دقیقاً همان شناسه‌های توکن نسخه v0.23 را تولید می‌کند و سازگاری عقب‌رو (Backward Compatibility) را تضمین می‌کند.

کاربران می‌توانند نسخه پیش-انتشار را از طریق Cargo با دستور cargo add tokenizers --pre نصب کنند. برای کسانی که فقط به رمزگذاری نیاز دارند و می‌خواهند وابستگی‌های آموزشی C++ را حذف کنند، استفاده از پرچم --no-default-features --features http توصیه می‌شود.

برای پردازش دسته‌ای، encode_batch فراخوانی اصلی است که روی هسته‌ها مقیاس می‌پذیرد. بایندینگ‌های پایتون همین کد Rust را در بر می‌گیرند، هرچند سربار هر فراخوانی (Per-call overhead) را اضافه می‌کنند که در این اندازه‌گیری‌های خام لحاظ نشده است.

تحلیل: پایان گلوگاه CPU

این به‌روزرسانی این فرض بنیادی را که توکن‌سازی «به اندازه کافی ارزان است که نادیده گرفته شود» تغییر می‌دهد. با تبدیل توکن‌سازی به یک مسئله محاسبات با عملکرد بالا (HPC) — با استفاده از SIMD، موازی‌سازی بدون قفل (Lock-free) و حلقه‌های بدون تخصیص حافظه — Hugging Face اکوسیستم را برای نسل بعدی موتورهای استنتاج فوق‌سریع آماده می‌کند. این نیاز به بهینه‌سازی در حالی رخ می‌دهد که تقاضا برای پردازش توکن‌ها به شدت افزایش یافته است؛ برای مثال، رشد ۲۵ هزار درصدی مصرف توکن در OpenRouter نشان‌دهنده فشار عظیم بر زیرساخت‌های توکن‌سازی در مقیاس جهانی است.

برای توسعه‌دهندگان، این به معنای تأخیر کمتر برای پنجره‌های متنی طولانی (Long-context windows) و توان عملیاتی (Throughput) بالاتر برای پردازش دسته‌ای است. اثر مرتبه دوم آن، کاهش کل اتلاف محاسباتی است؛ وقتی GPUها بیکار نمی‌مانند، چرخه‌های آموزش سریع‌تر به پایان می‌رسند و هزینه کمتری دارند.

مسیر رسیدن به نسخه 1.0.0

چندین ویژگی در نسخه RC پیاده‌سازی شده‌اند، از جمله بایندینگ‌های Node.js، رمزگشایی (Decoding) سریع‌تر که بایت‌ها را مستقیماً در بافرهای قابل استفاده می‌نویسد تا از رشته‌های میانی و کپی‌های اضافی جلوگیری شود، و افزودن FlatCache، MPHF RankStore، ادغام افزایشی (Incremental merging) و BucketVocabStore (#2190, #2188).

پیش از انتشار نهایی نسخه 1.0.0، تیم برنامه‌های زیر را دارد:

  • یکپارچه‌سازی رمزگذاری: استفاده از tk-encode در طول اعتبارسنجی آموزش، تا آموزش و استنتاج نتایج متفاوتی تولید نکنند.
  • بهینه‌سازی متادیتا: اختیاری کردن آفست‌ها و ماسک‌ها، و محاسبه این متادیتاها تنها در صورت درخواست، تا مسیر «فقط شناسه‌های توکن» سبک بماند.
  • بازطراحی نرمال‌سازها: افزودن پشتیبانی bitnorm بر پایه atomnorm (#2209) و گنجاندن پشتیبانی پیش-کامپایل شده SPM.
  • بهبود بایندینگ‌ها: ساده‌سازی بایندینگ‌های پایتون برای کاهش قفل‌ها، انواع Wrapper و کدهای Dispatch دست‌نویس، در حالی که subclassing، سریال‌سازی، رمزگشاهای سفارشی و پشتیبانی از CPython بدون رشته (Free-threaded) حفظ شود. همچنین برنامه‌ریزی برای بایندینگ‌های C/C++ مخصوص استنتاج برای ExecuTorch و llama.cpp و احتمالاً بایندینگ‌های JVM، Swift و Go وجود دارد.

در افق دورتر، Hugging Face در حال بررسی tok-devices است که رمزگذاری و رمزگشایی دسته‌ای را مستقیماً به GPU منتقل می‌کند. این کار اجازه می‌دهد واژگان یک بار آپلود شوند، موقعیت‌های خروجی به‌طور موازی محاسبه شوند و بایت‌ها روی GPU جمع‌آوری شوند، که به‌طور بالقوه CPU را برای دسته‌های بزرگ کاملاً از مسیر رمزگذاری حذف می‌کند. این بخش همچنان یک مؤلفه اختیاری است که نیاز به نمونه‌سازی‌های بیشتر دارد.

چرا این موضوع مهم است؟

این تغییر با تکیه بر تخصص مهندسی سیستم‌های توزیع‌شده، اتلاف منابع محاسباتی را کاهش می‌دهد. وقتی GPUها دیگر منتظر CPU نمی‌مانند، هزینه آموزش و استنتاج در مقیاس صنعتی به‌طور ملموسی افت می‌کند.

تأثیر برای ایران

برای توسعه‌دهندگان ایرانی که از مدل‌های بازمتن روی سخت‌افزارهای محدود استفاده می‌کنند، این بهینه‌سازی به معنای بهره‌وری بیشتر از CPUهای موجود و کاهش تأخیر در سرویس‌های محلی است.

·نگاه ما
تحریریه دات‌هوش

این به‌روزرسانی نشان می‌دهد که در عصر مدل‌های فوق‌سریع، بهینه‌سازی‌های سطح پایین (Low-level) در زبان‌هایی مثل Rust دیگر یک انتخاب نیستند، بلکه برای بقای زیرساخت‌ها ضروری‌اند. با حذف گلوگاه CPU، در واقع سقف توان عملیاتی (Throughput) کل سیستم جابه‌جا شده و مسیر برای مدل‌هایی با پنجره‌های متنی میلیونی هموارتر می‌شود. به نظر ما، این حرکت پیش‌درآمدی بر یکپارچگی کامل توکن‌سازی در سخت‌افزار است.

منابع

این گزارش با خط‌لولهٔ خودکار دات‌هوش از منابع معتبر جهانی تدوین و زیر نظر تحریریه منتشر شده است. روش کار ما

گفتگو

پنج‌شنبه‌های هوش‌محور

بسته‌ی هفتگی دات‌هوش

۵ خبر، ۲ ابزار، ۱ پرامپت در هر شماره. به‌زودی راه‌اندازی می‌شود — هر پنج‌شنبه صبح.

خبر کلیدی
ابزار کاربردی
پرامپت حرفه‌ای
تحلیل پژوهش
به‌زودی
زاویه‌ی ایرانی
به‌زودی
تمرین این هفته
به‌زودی

راهنماهای دات‌هوش

راهنماهای کاربردیِ دات‌هوش برای کار با هوش مصنوعی — از همین‌جا شروع کنید:

دات‌هوش

راهنمای فارسی هوش مصنوعی — با نگاه به ایران

اخبار روزانه، معرفی ابزارها و مدل‌ها، و آموزشِ کار با هوش مصنوعی؛ همیشه با این پرسش که از ایران چه چیزی کار می‌کند و چه چیزی نه.