-->
مقایسه‌ی توابع حلقه در PHP

مقایسه‌ی توابع حلقه در PHP

هم‌خوان کنید در:

زبانِ PHP، توابع و دستورات مختلفی برای کار با حلقه‌ها و آرایه‌ها در اختیار دارد که در وضعیت‌های مختلف و برای حل مشکلات مختلف می‌توانید از هر یک از آن‌ها استفاده کنید. در نگاه اول به نظر می‌رسد که تمام آن‌ها، کاری مشابه هم انجام می‌دهند. همه‌ی آن‌ها در یک حلقه‌ی متناهی دستوراتی را برای حصول نتیجه انجام می‌دهند! در زیر به تعدادی از مشهورترینِ این توابع و دستورات اشاره‌شده است:

فارغ از اینکه هرکدام از توابعِ فوق چه خصوصیات و امکاناتی را در اختیار شما قرار می‌دهند، آیا تابه‌حال به این فکر کرده‌اید که برای یک تکلیفِ مشخص، کدام‌یک سریع‌تر و بهینه‌تر هستند؟

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

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

برای درک بیشتر و مقایسه‌ی بهتر این توابع و دستورات، اجازه دهید فرض کنیم که یک مجموعه‌ی آرایه‌ای با یک‌میلیون عضو در اختیارداریم. در ادامه، فرآیندِ حلقه زدن بین این یک‌میلیون عضو، با استفاده از هر یک از توابع و دستورات فوق را باهم مقایسه خواهیم کرد. هدف، مقایسه‌ی سرعتِ هر یک از توابع و دستورات در اجرای حلقه است. میزان حافظه‌ای که هر یک در انجام این فرآیند صرف می‌کنند نیز حائز اهمیت است.

– تمام کدهای موجود در این نوشتار را می‌توانید در اینجا ببینید.

خطِ اول به PHP می‌گوید که حافظه‌ی مُجاز خود را به ۵۰۰ مگابایت افزایش دهد تا با مشکل کمبودِ حافظه مواجه نشویم. خطوط بعدی آرایه‌ای به طول یک‌میلیون، ایجاد می‌کند.

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

درجایی از دیسک، یک فایل به نام loop_speed_mem_test.php بسازید و کدهای زیر را درون آن تایپ کنید!

تایپ کنم؟! شوخی می‌کنی؟ چرا خیلی راحت Copy/Paste نکنم؟

خب، قطعاً برداشتن یک‌باره‌ی کدها و قرار دادنشان در فایل کار راحت‌تری است. من از فعلِ تایپ کردن استفاده می‌کنم، چون به نظرم در دنیای برنامه‌نویسی وقتی نمونه‌ی کدی را مجدداً خودتان تایپ می‌کنید، منطق و خروجی آن را بهتر یاد خواهید گرفت. به‌هرحال همیشه در Copy/Paste آزادید! 🙂

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

نکته در خصوص تکنیک ارجاع در foreach: برای تغییر در مقادیرِ هر یک از عناصرِ یک آرایه – بدون تعریف آرایه‌ی واسط، می‌توانید از ترفندِ ارجاع، در حلقه‌ی foreach استفاده کنید. این ترفند به‌شدت بهینه و حافظه دوست است! (به قطعه‌ی foreach_loop_ref در کدِ فوق دقت کنید.)

مقدارِ برچسبِ goto را به ترتیب بر روی array_filter، array_map، array_walk، foreach_loop، foreach_loop_ref، for_loop، while_loop تنظیم کرده، هر بار فایل را ذخیره و آن را در خط فرمان اجرا کنید:

خروجی برای array_filter، چیزی شبیه این خواهد بود:

جدول زیر نتایج ادغام‌شده‌ی توابع را پس از سه مرحله تست و میانگین‌گیری، یکجا نمایش می‌دهد:

احتمالاً شما هم مثلِ من شگفت‌زده شده‌اید. اختلاف‌ها، هم در ستون سرعت و هم در ستون حافظه، چشم‌گیر و قابل‌تأمل هستند. ضرب‌المثلی هست که می‌گوید: «یک عکس از هزاران کلمه بهتر است»:

php_loop_speed_compair_1M

php_loop_mem_compair_1M

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

خب، اگر تعداد عناصرِ آرایه را کمتر کنیم چه اتفاقی می‌افتد؟ مثلاً اگر به‌جای استفاده از یک آرایه‌ی یک‌میلیون عضوی، از یک آرایه‌ی صد هزار عضوی یا کمتر استفاده کنیم، چه؟! آیا اعداد و ارقام تغییر خواهند کرد؟ نمودارهای زیر نمایانگرِ نتایج برای یک آرایه‌ی صد هزار عضوی است:

php_loop_spedd_compair_100k

php_loop_mem_compair_100K

همان‌طور که مشاهده می‌کنید تمامِ مقادیر، کاهش محسوسی داشته و به شکل معنی‌داری حدود ۱۰ برابر کمتر شده‌اند. کمترین سرعت به زیر ۰/۶ ثانیه و بیشترین حافظه‌ی مصرفی به زیر ۱۰ مگابایت تقلیل یافته است. البته تغییری در نسبت‌ها و رتبه‌ها ایجاد نشده است. توابع سریع، همچنان سریع و توابع کند، همچنان کند باقی‌مانده‌اند!

سخن پایانی

در آن سال‌های دور و سخت، من اشتباه فکر می‌کردم! توابعِ توکارِ PHP که مخصوصِ کار با آرایه‌ها هستند، لزوماً سریع‌تر و بهینه‌تر نبودند!

خب، پس باید کار با آن‌ها را فراموش کرد؟ یقیناً نه!

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

تصور کنید اگر عناصرِ آرایه را در مثالِ فوق کوچک‌تر کنیم چه روی خواهد داد؟ مثلاً طولِ آن را به ۱۰۰ کاهش دهیم! خواهید دید که اختلاف‌ها به‌شدت کاهش‌یافته و قابل‌چشم‌پوشی است. از زاویه‌ی دیدِ کاربر و نرم‌افزار، در محیطی متعارف، اختلافِ ۰/۰۰۰۵۲ ثانیه و ۰/۰۰۰۰۳۲ ثانیه (تفاوت سرعت بین تابع array_filter و دستورِ foreach برای آرایه‌ی ۱۰۰ تایی) مطلقاً محسوس نیست! تجربه نشان داده است که در بیشترِ پروژه‌ها با آرایه‌ها و حلقه‌های خیلی کوچک و متوسط سروکار خواهیم داشت، بنابراین در این مواقع می‌توانید از سادگی، خوانایی و قابلیت‌های منحصربه‌فرد توابعِ توکارِ PHP بهره گرفته و نگرانِ کارایی و سرعت کد خود نباشید.


پی‌نوشت: احتمالاً تست‌های فوق در محیط‌ها و با داده‌های مختلف، نتایجِ متفاوتی ازآنچه در این نوشتار مشاهده می‌کنید خواهند داشت، ولی در شرایط یکسان، نسبت‌ها و رتبه‌ها یکسان است. اگر تجربیات مشابهی در این زمینه داشته‌اید در بخش دیدگاه‌ها بیان کنید.


هم‌خوان کنید در:
سهیل صمدزاده

سهیل صمدزاده

من سهیل صمدزاده؛ تحلیلگر نرم‌افزار، برنامه‌نویس، وبلاگ نویس و مدیر چند پروژه‌ی نرم‌افزاری هستم. به فنّاوری و تفکرات چابک در توسعه نرم‌افزار علاقه‌مندم و سعی می‌کنم خُرده دانش‌های خودم رو اینجا به اشتراک بذارم.

دیدگاه‌های شما ارزشمند‌اند...

اولین نفری باشید که دیدگاه می‌گذارد.

خبر بده وقتی
avatar
1024

وی‌پی‌دیسکاز

به کانال تلگرام آیلِتـــ بپیوندید!

t_logo

آیلِتـــ هر ماه در صندوق ایمیل شما: