اگر امروز برای بهینهسازی سرعت عاملهای خود فقط روی مدلها تمرکز کردهاید، احتمالاً متهم اصلی را اشتباه گرفتهاید. طبق یافتههای یک آزمایش پروفایلینگ که در ۲۳ سپتامبر ۲۰۲۶ انجام شد، عامل اصلی کندی در حلقههای عاملمحور، نه استنتاج مدل، بلکه زمان بازسازی پرامپتها است. توسعهدهندگان معمولاً تأخیر شبکه یا توقفهای GPU را مقصر گلوگاههای مقیاسپذیری میدانند، اما این ابزار پروفایلینگ سفارشی نشان میدهد که کندی اصلی در نحوه ناکارآمد اتصال تاریخچه (Concatenation) پیش از هر فراخوانی جدید مدل رخ میدهد. نویسنده پس از روزها تلاش بیهوده برای بهینهسازی استنتاج، متوجه شد که بازسازی پرامپت در هر دور از اجرای حلقه، زمان را میبلعد. این موضوع یک پرسش اساسی را ایجاد میکند: آیا شما مدل را تنظیم کردهاید اما از بهینهسازی عملیات کپی دادهها غافل شدهاید؟
بسیاری از توسعهدهندگان با مدل مانند یک جعبه سیاه برخورد میکنند و تصور میکنند زمان در «سیمهای ارتباطی» یا شبکه گم میشود. اما در واقعیت، هرچه یک عامل بیشتر با ابزارها تعامل میکند، تاریخچه گفتگو رشد میکند. اگر کد شما این تاریخچه را با روشهای ناکارآمد بازسازی کند، سربار آمادهسازی متن میتواند از زمانی که مدل برای تفکر نیاز دارد، بیشتر شود. این یادداشت یک گزارش آزمایشگاهی است، نه یک داستان تجاری از محیط تولید، و بر اساس ردپاهای عملیاتی (Production Traces) نیست. نویسنده محیطی را طراحی کرده است که میتوان آن را همین امشب اجرا کرد تا این شکست دقیق را بصری کرد.
تصور کنید گلولهای از برف از تپهای پایین میغلتد. در ابتدا، این گلوله بسیار کوچک و ناچیز است، تقریباً مانند یک خطای گرد کردن. اما هرچه عامل خروجیهای ابزارهای بیشتری را جمعآوری میکند، «کار کپی» مدیریت آن متن، خشن و سنگین میشود. این همان «نقطه تلاقی» است؛ جایی که هزینه اداری پرامپت بر هوش مدل غلبه میکند. هدف این است که تنها یک نمودار را پیدا کنیم که بتوان آن را نگه داشت و بقیه نویزهای موجود در انبوهی از داشبوردها را دور ریخت. نویسنده برای ثبت این تلاقی، زمان بازسازی را در مقابل یک توقف شبیهسازی شده (Stubbed Nap) برای مدل ترسیم کرد.
مکانیزم پروفایلینگ
برای اثبات این ادعا، یک محیط تست طراحی شد که حلقه عامل را به چهار بخش مجزا و نامگذاری شده تقسیم میکند. در این آزمایش از میانگینها اجتناب شد، زیرا میانگینها در ماه گذشته به نویسنده دروغ گفته بودند؛ در عوض، از بازههای قابل انباشت با نامهای ساده استفاده شد و نتایج در یک فایل CSV واحد ذخیره گردید. این فایل CSV همان نموداری است که برای تحلیل نهایی نگه داشته شد:
- سریالسازی (Serialize): زمان صرف شده برای تبدیل نتایج ابزارها به رشتههای JSON از طریق تابع
json.dumps. - ابزار (Tool): زمان اجرای واقعی ابزار محلی (که در اینجا با یک جایگزین ۵ میلیثانیهای شبیهسازی شد).
- بازسازی (Rebuild): زمان صرف شده برای اتصال تاریخچه به پرامپت نهایی.
- مدل (Model): زمان پرش شبکه یا زمان توقف شبیهسازی شده برای LLM.
خطکش مدلهای شبیهساز
با استفاده از یک «مدل شبیهساز» (Stub Model) — تابعی که صرفاً ۴۰ میلیثانیه توقف (Sleep) دارد — نویسنده یک خطکش ثابت برای اندازهگیری ایجاد کرد. تأکید میشود که این توقف یک خطکش است، نه یک بنچمارک برای سرعت مدل؛ لذا نباید به عنوان سرعت واقعی مدل نقل شود. وقتی زمان بازسازی پرامپت از این آستانه ۴۰ میلیثانیهای عبور کرد، کاملاً مشخص شد که سیستم زمان بیشتری را صرف دستکاری رشتهها میکند تا استنتاج شبیهسازی شده. این شرمساری — جایی که تابع مدل در برابر مسیر بازسازی شکست خورد — دلیل اصلی نگه داشتن تصویر نهایی بود.
تلهی مرتبه دوم (Quadratic Trap)
این ابزار تست بهطور خاص یک خطای رایج کدنویسی را برجسته میکند: اتصال مرتبه دوم (Quadratic Join). بسیاری از توسعهدهندگان حلقههایی مینویسند که بهطور مکرر پیشوندهای تاریخچه را متصل میکنند و باعث ایجاد یک پرتگاه عملکردی میشوند. در کد پایتون ارائه شده، تابع rebuild_prompt عمداً از حلقهای استفاده میکند که برشهای تاریخچه را متصل میکند (prompt = "\n".join(history[: i + 1]))؛ این الگو باعث میشود با افزایش تعداد دورها، زمان اجرا بهطور انفجاری رشد کند. این اتصال مرتبه دوم به عنوان یک میکروسکوپ برای نمایش مشکل به کار رفته است، نه به عنوان یک توصیه کدنویسی.
برای تحلیل دقیقتر متهم، میتوان حلقه را در cProfile قرار داد. در حالی که بازهها (Spans) یک خط زمانی ارائه میدهند، توابع متهم اصلی را فاش میکنند. در این اجراها، تابع json.dumps اغلب اولین کسی است که زیر نورافکن قرار میگیرد. استخراج pstats یک فلات گسترده را زیر تابع rebuild_prompt در اجراهای شبیهساز نشان میدهد، در حالی که نوار stub_model نازک و صادق باقی میماند. نویسنده همچنان فایل CSV انباشته را به نمودارهای شعلهای (Flame Graph) ترجیح میدهد، زیرا بدون نیاز به زیرساختهای گرافیکی در یادداشتها جای میگیرد و توهینِ ناشی از کندی سیستم، در کپی-پیست کردن نیز به خوبی باقی میماند.
اثر حجم دادههای ابزارها
حجم بالای دادههای خروجی از ابزارها مانند «سیمان خیس» در هنگام سریالسازی عمل کرده و سرعت را میگیرد. در این آزمایش برای تورم دادهها از Payloadهای حجیم JSON استفاده شد، که بهطور خاص شامل موارد زیر بود:
- فهرستی از ۵۰ فایل که هر کدام دارای یک مسیر (مثلاً
src/mod_i.py) و یک پیشنمایش متشکل از ۲۰۰۰ کاراکتر 'x' بودند. - یک ورودی لاگ متشکل از ۴۰۰ کاراکتر 'ok'.
این تورم باعث میشود تابع json.dumps به شدت در معرض دید قرار گیرد. نویسنده اشاره میکند که اگرچه ابزارهای واقعی باید دادههای کمتری برگردانند، اما حجیم کردن Payload یک ترفند آزمایشگاهی است تا نوارها با هم «بجنگند» و گلوگاه را آشکار کنند. خروجیهای شبیهساز ممکن است در سطح محلی نویزی به نظر برسند، اما درس اصلی در شکل نمودار است، نه در ارقام. برای مثال، در دور اول ممکن است دو هش (#) برای بازسازی و نقاط زیادی برای مدل دیده شود، اما تا دور ۱۲ام، نوار بازسازی به رشتهای طولانی از هشها تبدیل میشود در حالی که مدل همچنان یک خط کوتاه از نقاط باقی میماند.
تست با نقاط انتهایی واقعی
برای اعتبارسنجی یافتهها فراتر از یک شبیهساز محلی، محیط تست به MonkeyCode متصل شد؛ پلتفرمی که دسترسی رایگان به مدلها و گزینههای سرور را فراهم میکند. این کار اجازه داد تا حلقه از لپتاپ محلی خارج شود بدون اینکه نیازی به یک باکس GPU باشد. افشا: این مقاله به عنوان بخشی از فعالیتهای ترویجی محصولات MonkeyCode تهیه شده است. نویسنده تأخیرها، نام مدلها یا سهمیههای خود را منتشر نمیکند و این را یک بنچمارک محصول نمیداند.
کلاینت برای ارسال پرامپت از طریق HTTP از urllib.request استفاده میکند تا با اجتناب از SDKهای سازندگان، اندازهگیریها پاک و بدون اثرات جانبی باشد. تابع remote_model مقادیر را از متغیر محیطی AGENT_MODEL_URL میخواند و از یک مهلت زمانی (Timeout) ۳۰ ثانیهای استفاده میکند.
مشاهدات کلیدی از تستهای راه دور عبارتند از:
- تأخیر دور اول: دستتکانیهای (Handshakes) TLS و DNS اغلب دور اول را میدزدند و جهشی ایجاد میکنند که نماینده عملکرد حالت پایدار نیست.
- تأخیرهای صف: سرورهای اشتراکی زمانهای انتظار نامرئی ایجاد میکنند. بدون ردپاهای سمت سرور، تنها میتوان سفر رفتوبرگشت (Round Trip) اندازهگیری شده را متهم کرد، نه خود مدل را.
- جملات رشد: حتی با مدلهای راه دور، زمان بازسازی همچنان جمله رشد اصلی است، زیرا کل تاریخچه در هر دور دوباره ارسال میشود. این محیط تست توکنها را استریم نمیکند؛ اگر یک عامل استریم میکند، رمزگشا (Decoder) باید بهطور جداگانه ابزارگذاری شود. از نمودار شبیهساز برای موارد استریم استفاده نکنید.
بهینهسازی و راهکارها
راه حل این گلوگاه ساده است: بازسازی از پیشوندها را متوقف کنید. جایگزینی اتصال مرتبه دوم با یک فراخوانی واحد "\n".join(history) میتواند باعث شود نوار بازسازی مانند یک چادر ارزانقیمت فرو بریزد. اگر پس از این اصلاح، عملکرد همچنان کند بود، توسعهدهندگان باید به سریالسازی Payloadهای حجیم ابزارها نگاه کنند.
این روش پروفایلینگ به عنوان یک میکروسکوپ برای «کارهای اداری» عاملها طراحی شده است. این روش برای پرامپتهای بسیار کوچک — مثلاً یک چت ۲۰ توکنی — کاربردی ندارد و مشکل بازسازی را نشان نخواهد داد. همچنین این روش توقفهای هسته GPU یا رفتارهای عجیب توکنساز را پیدا نمیکند. از آنجایی که ساعتهای لپتاپ نویزی هستند و فنها میتوانند بر عملکرد تأثیر بگذارند، نویسنده توصیه میکند تست را سه بار اجرا کرده و در صورت نیاز به دقت بالا، فرکانس CPU را ثابت (Pin) کنید.
در نهایت، این محیط تست به عنوان یادآوری عمل میکند تا پیش از آنکه دوباره مدل استنتاج را به چالش بکشید، کارهای کپی و اداری را اصلاح کنید. وقتی نوار بازسازی از خطکش عبور کرد، شما یک باگ دارید. کپی را اصلاح کنید. سپس دوباره به سراغ بهینهسازی استنتاج بروید.
گام بعدی شما
- کد بازسازی پرامپت خود را بررسی کنید و هرگونه حلقه تکراری برای اتصال رشتهها را حذف کنید.
- حجم خروجی ابزارهای خود را محدود کنید تا زمان سریالسازی JSON افزایش نیابد.
- از ابزارهای پروفایلینگ مانند
cProfileبرای شناسایی توابع کند در لایهی آمادهسازی متن استفاده کنید.
اما داستان سختافزاری این تحول حتی شگفتانگیزتر است — به تحلیل ما دربارهی تراشههای Blackwell مراجعه کنید.




گفتگو