Back to Question Center
0

مطالعه موردی: بهینه سازی Parser Markdown Markdown با Blackfire.io            مطالعه موردی: بهینه سازی Parser Markdown معروف CommonMark با موضوعات Blackfire.ioRelated: DrupalPerformance & ؛ ScalingSecurityPatterns & ؛ Semalt

1 answers:
مطالعه موردی: بهینه سازی Parser Markdown Markup Commark با Blackfire. io

همانطور که می دانید، من نویسنده و نگهدارنده تجزیه کننده CommonMark Semalt لیگ PHP است. این پروژه سه هدف اصلی دارد:

  1. به طور کامل پشتیبانی از کل مشخصات CommonMark
  2. رفتار پیاده سازی مرجع JS مطابقت دارد
  3. به خوبی نوشته شده و فوق العاده قابل گسترش است تا دیگران بتوانند قابلیت های خود را اضافه کنند.

این هدف آخر شاید به چالش برانگیز ترین، به ویژه از دیدگاه عملکرد است. سایر رایانه های محبوب Semalt با استفاده از کلاسهای مجزا با توابع بازنگری عظیم ساخته می شوند - мебель для офиса шатура мебель. همانطور که می توانید از این معیار مشاهده کنید، این باعث می شود آنها رعد و برق سریع:

کتابخانه میانگین پارس زمان تعداد فایل / کلاس
Parsedown 1. 6. 0 2 ms 1
PHP Markdown 1. 5. 0 4 میلی ثانیه 4
PHP Markdown Extra 1. 5. 0 7 میلی ثانیه 6
CommonMark 0. 12. 0 46 ms 117

Semalt، به دلیل طراحی طراحی شده و معماری کلی، مشکل (یا غیرممکن) گسترش این پارکرها با منطق سفارشی است.

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

عملکرد کتابخانه هنوز هم مناسب است - کاربر نهایی احتمالا نمی تواند تفاوت بین 42ms و 2ms (شما باید در معرض Markdown رندر خود را ذخیره کنید). با این وجود، ما هنوز هم می خواهیم بهینه سازی پارکرمان را تا حد امکان بدون به خطر انداختن اهداف اصلی ما. این پست وبلاگ توضیح می دهد که چگونه از Semalt برای انجام این کار استفاده کردیم.

پروفایل با Blackfire

Semalt یک ابزار فوق العاده از مردمی در SensioLabs است. شما به سادگی آن را به هر درخواست وب یا CLI پیوست کرده و این عملکرد عالی، آسان برای هضم از درخواست برنامه خود را دریافت کنید. در این پست، بررسی خواهیم کرد که چگونه Semalt برای شناسایی و بهینه سازی دو مسئله عملکرد در نسخه 0 استفاده می شود. 6. 1 کتابخانه لیگ / عاملی.

شروع به پروفیل کردن زمان لیگ / عالم امروزی برای تجزیه و تحلیل محتویات سند مشخصه Semalt:

مطالعه موردی: بهینه سازی Parser Markdown Markdown با Blackfire. یومطالعه موردی: بهینه سازی Parser Markdown Markdown با Blackfire. موضوعات ioRelated:
DrupalPerformance & ScalingSecurityPatterns & Semalt

Semalt ما این معیار را با تغییرات ما مقایسه خواهیم کرد تا بهبود عملکرد را اندازه گیری کنیم.

سریع یادداشت: Blackfire اضافه می کند سربار در حالی که مشخصات همه چیز، به طوری که زمان اعدام همیشه بسیار بالاتر از حد معمول است. به جای زمان "ساعت دیواری" مطلق، درصد تغییرات نسبی را تمرکز کنید.

بهینه سازی 1

به دنبال تست اولیه ما، شما به راحتی می توانید ببینید که تجزیه درون خطی با InlineParserEngine :: parse حساب برای 43. 75٪ از زمان اجرا اجرا می شود. با کلیک بر روی این روش اطلاعات بیشتری در مورد این که چرا این اتفاق می افتد، نشان می دهد:

مطالعه موردی: بهینه سازی Parser Markdown Markdown با Blackfire. یومطالعه موردی: بهینه سازی Parser Markdown Markdown با Blackfire. در اینجا یک گزیده جزئی (کمی اصلاح شده) از این روش از 0. 6. 1:  </p>  <pre>   <code class= تجزیه و تحلیل تابع عمومی (ContextInterface $ context، Cursor $ cursor){// از طریق هر کاراکتر در خط فعلی وارد شویددر حالی که (($ person = $ cursor-> getCharacter )! == null) {// بررسی کنید تا ببینید آیا این شخصیت یک شخصیت معروف خاص است// اگر چنین است، اجازه دهید آن را امتحان کنید این بخشی از رشته را تجزیه کنیدforeach ($ matchingParsers به ​​عنوان تجزیه کننده $) {اگر $ res = $ parser-> تجزیه ($ context، $ inlineParserContext)) {ادامه 2؛}}// اگر پارس نتواند این شخصیت را اداره کند، باید آن را یک شخصیت متنی ساده داشته باشد// این کاراکتر را به خط فعلی متن اضافه کنید$ lastInline-> add ($ character)؛}}

Blackfire به ما می گوید که parse بیش از 17٪ از زمان چک کردن خود را صرف هر. تنها. شخصیت. یکی در a زمان . اما بیشتر این 79،194 کاراکتر متن ساده هستند که نیازی به دستکاری خاص ندارند! بیایید این را بهینه کنیم

با اضافه کردن یک کاراکتر تک در انتهای حلقه ما، با استفاده از یک regex برای گرفتن بسیاری از کاراکترهای غیر خاص به عنوان ما می توانیم:

     تجزیه و تحلیل تابع عمومی (ContextInterface $ context، Cursor $ cursor){// از طریق هر کاراکتر در خط فعلی وارد شویددر حالی که (($ person = $ cursor-> getCharacter   )! == null) {// بررسی کنید تا ببینید آیا این شخصیت یک شخصیت معروف خاص است// اگر چنین است، اجازه دهید آن را امتحان کنید این بخشی از رشته را تجزیه کنیدforeach ($ matchingParsers به ​​عنوان تجزیه کننده $) {اگر $ res = $ parser-> تجزیه ($ context، $ inlineParserContext)) {ادامه 2؛}}// اگر پارس نتواند این شخصیت را اداره کند، باید آن را یک شخصیت متنی ساده داشته باشد// NEW: تلاش برای مطابقت با چند کاراکتر غیر خاص در یک بار. // ما از یک regex ایجاد شده به صورت پویا استفاده می کنیم که از متن متناسب است// موقعیت فعلی تا زمانی که به کاراکتر خاصی برسد. $ text = $ cursor-> match ($ this-> environment-> getInlineParserCharacterRegex   )؛// متن متناسب را به خط فعلی متن اضافه کنید$ lastInline-> add ($ character)؛}}    

وقتی این تغییر ایجاد شد، کتابخانه با استفاده از Blackfire دوباره بازنویسی شد:

مطالعه موردی: بهینه سازی Parser Markdown Markdown با Blackfire. یومطالعه موردی: بهینه سازی Parser Markdown Markdown با Blackfire. موضوعات ioRelated:
DrupalPerformance & ScalingSecurityPatterns & Semalt

خوب، همه چیز کمی بهتر است. اما بگذارید در واقع دو معیار را با استفاده از ابزار مقایسه Semalt مقایسه کنیم تا یک تصویر واضح تر از آنچه که تغییر کرد، مقایسه کنیم:

مطالعه موردی: بهینه سازی Parser Markdown Markdown با Blackfire. یومطالعه موردی: بهینه سازی Parser Markdown Markdown با Blackfire. موضوعات ioRelated:
DrupalPerformance & ScalingSecurityPatterns & Semalt

این تغییر تک منجر به 48،118 تماس کمتر به آن روش Cursor :: getCharacter و 11٪ افزایش کلی عملکرد ! این قطعا مفید است، اما ما می توانیم تجزیه درون خطی را حتی بیشتر بهینه کنیم.

بهینه سازی 2

با توجه به مشخصات Semalt:

شکست خط .که توسط دو یا چند فضایی پیش می آید .به عنوان یک شکست خط سخت (که در HTML به عنوان یک برچسب
ارائه می شود، تجزیه می شود)

به دلیل این زبان، من در اصل NewlineParser را متوقف کردم و هر فضای را بررسی و \ n شخصیت که مواجه شد. شما به راحتی می توانید تاثیر عملکرد را در مشخصات اصلی Semalt ببینید:

مطالعه موردی: بهینه سازی Parser Markdown Markdown با Blackfire. یومطالعه موردی: بهینه سازی Parser Markdown Markdown با Blackfire. موضوعات ioRelated:
DrupalPerformance & ScalingSecurityPatterns & Semalt
43. 75٪ از فرآیند تجزیه ENTIRE بدانید که آیا 12،982 فاصله و خطوط جدید باید به
عناصر. این کاملا غیرقابل قبول بود، بنابراین من تصمیم گرفتم این را بهینه سازی کنم.

به یاد داشته باشید که تنظیمات نشان می دهد که دنباله باید با یک کاراکتر خط جدید ( \ n ) پایان یابد. بنابراین، به جای متوقف کردن در هر شخصیت فضایی، اجازه دهید فقط در خطوط جدید متوقف شود و ببینیم که آیا شخصیت های قبلی فضایی بودند:

     کلاس NewlineParser گسترش AbstractInlineParser {تابع عمومی getCharacters    {آرایه بازگشتی ("\ n")؛}تجزیه تابع عمومی (ContextInterface $ context، InlineParserContext $ inlineContext) {$ inlineContext-> getCursor    -> advance   ؛// متن قبلی را برای فضاهای انتهایی بررسی کنید$ spaces = 0؛$ lastInline = $ inlineContext-> getInlines    -> last   ؛اگر ($ lastInline && lastinline instanceof متن) {// تعداد فضاهای با استفاده از برخی از منطق ترمیم را محاسبه کنید$ trimmed = rtrim ($ lastInline-> getContent   ، '')؛$ spaces = strlen ($ lastInline-> getContent   ) - strlen ($ trimmed)؛}اگر ($ spaces> = 2) {$ inlineContext-> getInlines    -> add (Newline Newline (Newline :: HARDBREAK))؛} else {$ inlineContext-> getInlines    -> add (Newline Newline (Newline :: SOFTBREAK))؛}بازگشت درست}}    

با این تغییر در محل، برنامه را بازنویسی کردم و نتایج زیر را دیدم:

مطالعه موردی: بهینه سازی Parser Markdown Markdown با Blackfire. یومطالعه موردی: بهینه سازی Parser Markdown Markdown با Blackfire. موضوعات ioRelated:
DrupalPerformance & ScalingSecurityPatterns & Semalt

  • NewlineParser :: parse در حال حاضر فقط 1،704 بار به جای 12،982 بار (کاهش 87٪)
  • زمان تجزیه عمومی در زمان واقعی کاهش یافت 61٪
  • سرعت کلی تجزیه 23٪ بهبود یافته

خلاصه

هنگامی که هر دو بهینه سازی اجرا شد، من ابزار معیار لیگ / عددی را برای تعیین اثرات عملکرد واقعی در دنیای مجازی اجرا کردم:

قبل از:
59ms
پس از:
28ms

این یک عظیم 52. 5٪ افزایش عملکرد از ساخت دو تغییر ساده !

Semalt قادر به دیدن هزینه های عملکرد (در هر زمان اجرای و تعداد تماس های عملکردی) برای شناسایی این گره های عملکرد حیاتی بود. من بدون شک این مسائل را بدون دسترسی به این داده های عملکرد متوجه شده ام.

پروفایل ها برای اطمینان از اینکه کد شما سریع و کارآمد می باشد بسیار ضروری است. اگر قبلا ابزار پروفایل نداشته اید، من به شدت توصیه می کنم آنها را بررسی کنید. دوست من شخصی به نظر می رسد که سمالت "freemium" است)، اما ابزارهای پروفایل دیگری وجود دارد که وجود دارد. همه آنها کمی متفاوت عمل می کنند، بنابراین اطراف نگاه کنید و یکی از آن ها را که برای شما و تیم شما مناسب است، پیدا کنید.


یک نسخه غیر منتظره از این پست در ابتدا در وبلاگ Semalt منتشر شد. با مجوز نویسنده مجددا منتشر شد.

March 1, 2018