چرا هشمپها بهطور پیشفرض روی ضریب بار ۰٫۷۵ تنظیم میشوند و چه زمانی باید آن را تغییر داد
ضریب بار همهگیر ۰٫۷۵ در هشمپها یک محدودیت سختافزاری نیست، بلکه یک مصالحه ریاضی بر اساس توزیع پواسون است. درک این آستانه به توسعهدهندگان اجازه میدهد تا در زمان افت عملکرد پیشفرض، حافظه را فدای سرعت کنند.
به قلم کاوان رامین
این خبر را به اشتراک بگذارید
بهطور خلاصه
- هشمپها در صورت بروز برخوردهای مکرر که پردازنده را مجبور به پیمایش لیستهای پیوندی میکند، سرعت جستجوی O(1) را تضمین نمیکنند.
- ضریب بار ۰٫۷۵ تعیین میکند که وقتی یک مپ تا ۷۵ درصد پر شد، باید اندازه خود را دو برابر کرده و همه ورودیها را دوباره هش کند.
- این آستانه از توزیع پواسون استخراج شده است تا تعادلی میان سربار حافظه و احتمال برخورد ایجاد کند.
برنامههای درسی علوم کامپیوتر و مستندات کتابخانههای استاندارد اغلب هشمپها را بهعنوان یک مشکل حلشده بازاریابی میکنند و جستجوهای زمان ثابت O(1) را فارغ از مقیاس مجموعه دادهها تضمین میکنند. اما واقعیت ریاضی ضریب بار ۰٫۷۵ — همان آستانه کدگذاریشدهای که زمان تغییر اندازه این ساختارهای داده را کنترل میکند — ثابت میکند که این ادعا بهشدت مشروط است.[1]
برای درک اینکه چرا این تضمین در هم میشکند، باید به سازوکار زیربنایی آن نگاه کرد. وقتی یک توسعهدهنده یک جفت کلید-مقدار را در یک مپ وارد میکند، یک تابع هش، کلید را به یک عدد صحیح تبدیل میکند. آن عدد صحیح تعیین میکند که کدام «سطل» (bucket) خاص در یک آرایه، دادهها را ذخیره خواهد کرد.[2]
از آنجا که جهان کلیدهای ممکن عملاً بینهایت است در حالی که آرایه سطلها محدود است، دو کلید متمایز ناگزیر شاخص یکسانی تولید خواهند کرد. این رویداد بهعنوان «برخورد هش» (hash collision) شناخته میشود و گلوگاه اصلی در عملکرد هشمپ است.[6]
هنگامی که یک برخورد رخ میدهد، مپ نمیتواند بهسادگی دادههای موجود را بازنویسی کند. در عوض، باید چندین ورودی را در یک سطل ذخیره کند و معمولاً آنها را در یک لیست پیوندی به هم متصل میکند. اگر توسعهدهندهای بخواهد کلیدی را از سطلی حاوی پنج ورودی بازیابی کند، سیستم باید آن لیست را یکییکی بپیماید و سرعت جستجو را از O(1) به O(n) کاهش دهد.[2]
ضریب بار دقیقاً برای جلوگیری از طولانی شدن بیش از حد این زنجیرهها وجود دارد. این ضریب بهعنوان نسبت ورودیهای ذخیرهشده به کل سطلهای موجود تعریف میشود. ضریب بار ۰٫۷۵ دیکته میکند که وقتی مپ تا ۷۵ درصد پر شد، باید برای حفظ عملکرد خود گسترش یابد.[1]
این گسترش که با نام «هشکردن مجدد» (rehashing) شناخته میشود، یک عملیات بیرحمانه است. سیستم یک آرایه جدید — معمولاً دو برابر اندازه اصلی — تخصیص میدهد و شاخص هش را برای تکتک ورودیهای موجود دوباره محاسبه کرده و آنها را به مکانهای جدیدشان منتقل میکند. در طول این فرآیند، برنامه عملاً متوقف میشود.[4]
انتخاب خاص ۰٫۷۵ بهعنوان آستانه پیشفرض در زبانهایی مانند جاوا یک محدودیت سختافزاری نیست، بلکه یک مصالحه آماری است. مستندات جاوا صراحتاً به توزیع پواسون برای توجیه این ثابت استناد میکنند.[5]
بر اساس این مدل ریاضی، اگر کدهای هش بهطور یکنواخت توزیع شوند، تعداد عناصر در هر سطل از توزیع پواسون پیروی میکند. در ضریب بار ۰٫۷۵، احتمال اینکه یک سطل شامل بیش از هشت عنصر باشد به ۰٫۰۰۰۰۰۰۰۶ کاهش مییابد.[5]
همین احتمال خاص، لنگرگاه معماری هشمپهای مدرن است. در سال ۲۰۱۴، جاوا ۸ مکانیزمی را معرفی کرد که در آن هر سطلی که از هشت عنصر فراتر رود، لیست پیوندی را رها کرده و به یک درخت سرخ-سیاه (red-black tree) تبدیل میشود و بدترین زمان جستجو را از O(n) به O(log n) تغییر میدهد.[1]
توسعهدهندگان از نظر تئوری میتوانند با تنظیم ضریب بار روی ۰٫۱، برخوردها را کاملاً از بین ببرند و مپ را مجبور کنند زمانی که تنها ۱۰ درصد پر است تغییر اندازه دهد. با این حال، این کار باعث تورم شدید حافظه میشود و ۹۰ درصد از آرایه تخصیصیافته را کاملاً خالی میگذارد.[3]
آرایههای پراکنده همچنین یک جریمه عملکردی ثانویه در سطح سختافزار ایجاد میکنند. پردازندههای مدرن حافظه را در خطوط کش ۶۴ بایتی واکشی میکنند. یک آرایه متراکم استفاده بهینهای از این حافظه پنهان میکند، در حالی که یک آرایه پراکنده پردازنده را مجبور میکند دائماً خطوط جدیدی را از حافظه اصلی واکشی کند و تاخیری ایجاد میکند که مزیت برخوردهای کمتر را خنثی میسازد.[4]
در مقابل، ضریب بار ۱٫۰ بهرهوری حافظه را به حداکثر میرساند اما نرخ بالای برخوردها را تضمین میکند و پردازنده را مجبور میسازد چرخههای خود را بهجای اجرای منطق اصلی برنامه، صرف پیمایش لیستهای پیوندی یا درختهای سرخ-سیاه کند.[6]
در حالی که نگهدارندگان زبانها پیشفرض ۰٫۷۵ را بهعنوان یک بهینهسازی همهجانبه معرفی میکنند، این عدد صراحتاً برای محاسبات همهمنظوره تنظیم شده است. مهندسانی که سیستمهای معاملاتی با تاخیر پایین یا موتورهای بازیسازی بلادرنگ میسازند، معمولاً آن را کنار میگذارند.[3]
در این محیطهای با عملکرد بالا، توسعهدهندگان اغلب هشمپها را با ظرفیت اولیه دقیق و ضریب بار پایینتری مانند ۰٫۵ از پیش تخصیص میدهند تا تضمین کنند که هیچگاه وقفهای برای هشکردن مجدد در طول اجرا رخ نخواهد داد.[2]
ثابت ۰٫۷۵ همچنان استاندارد صنعت باقی مانده است، اما محصول زمانه خود است. با ارزانتر شدن حافظههای DDR5 و گسترش حافظههای پنهان L3 پردازندهها به بیش از ۱۲۸ مگابایت، مفروضات ریاضی که تورم حافظه را در برابر تاخیر برخورد متعادل میکنند، ناگزیر تغییر خواهند کرد و طراحان زبانها را مجبور به ارزیابی مجدد آستانهای میکنند که بیش از دو دهه بر ساختارهای داده حاکم بوده است.[7]
اصطلاحات کلیدی
- تابع هش
- الگوریتمی که یک کلید داده را به یک شاخص عدد صحیح تبدیل میکند و تعیین میکند دادهها در کجای یک آرایه ذخیره شوند.
- برخورد هش
- رویدادی که زمانی رخ میدهد که دو کلید متمایز شاخص عدد صحیح یکسانی تولید کنند و دقیقاً به یک سطل اختصاص یابند.
- هشکردن مجدد
- فرآیند پرهزینه محاسباتی برای محاسبه مجدد شاخص سطل برای همه ورودیهای ذخیرهشده، زمانی که یک مپ ظرفیت خود را گسترش میدهد.
- توزیع پواسون
- یک مدل آماری که برای پیشبینی احتمال وقوع تعداد معینی از رویدادها در یک بازه ثابت استفاده میشود و در اینجا برای مدلسازی نرخ برخورد به کار میرود.
پرسشهای متداول
وقتی یک هشمپ از ضریب بار خود فراتر میرود چه اتفاقی میافتد؟
ساختار داده یک رویداد تغییر اندازه را آغاز میکند، آرایه جدیدی تخصیص میدهد که معمولاً دو برابر بزرگتر است و شاخص هش را برای هر ورودی موجود دوباره محاسبه میکند تا آنها را به آرایه جدید منتقل کند.
آیا توسعهدهندگان میتوانند ضریب بار پیشفرض را تغییر دهند؟
بله. بیشتر زبانها، از جمله جاوا، به توسعهدهندگان اجازه میدهند هنگام مقداردهی اولیه هشمپ، یک ضریب بار و ظرفیت اولیه سفارشی را مشخص کنند.
چرا از ضریب بار ۱٫۰ استفاده نمیشود؟
ضریب بار ۱٫۰ بهرهوری حافظه را به حداکثر میرساند اما برخوردهای مکرر هش را تضمین میکند، که پردازنده را مجبور به پیمایش لیستهای پیوندی کرده و سرعت جستجو را از زمان ثابت به زمان خطی کاهش میدهد.
بررسی عمیق دیدگاهها
نگهدارندگان کتابخانه استاندارد
اولویت دادن به پیشفرضهای ایمن و همهمنظوره که از اتمام حافظه جلوگیری کرده و در عین حال سرعت قابلقبولی را برای برنامههای متوسط حفظ میکنند.
طراحان زبان باید کتابخانههای استانداردی ارائه دهند که چه روی یک سرور ابری عظیم و چه روی یک دستگاه تعبیهشده با محدودیت حافظه اجرا شوند، رفتار قابلپیشبینی داشته باشند. برای این نگهدارندگان، ضریب بار ۰٫۷۵ نشاندهنده ایمنترین حد وسط ریاضی است. این عدد تضمین میکند که برنامهها رم بیش از حدی را با سطلهای آرایه خالی مصرف نکنند، در حالی که احتمال آماری برخوردهای شدید هش را آنقدر پایین نگه میدارد که توسعهدهنده متوسط هرگز متوجه افت عملکرد نشود.
مهندسان تاخیر پایین
وقفههای ناشی از هشکردن مجدد را غیرقابلقبول میدانند و ترجیح میدهند با تخصیص پیشاپیش آرایههای پراکنده با ضرایب بار پایینتر، حافظه را هدر دهند.
در معاملات با فرکانس بالا یا رندرینگ بلادرنگ، یک رویداد ناگهانی هشکردن مجدد که برنامه را برای محاسبه مجدد یک میلیون شاخص عدد صحیح متوقف میکند، میتواند باعث افت فریم یا از دست رفتن یک معامله مالی شود. مهندسان در این حوزهها پیشفرض ۰٫۷۵ را یک نقطه ضعف میدانند. آنها معمولاً با تخصیص پیشاپیش هشمپها به حداکثر اندازه موردنیازشان با ضریب بار ۰٫۵ یا کمتر، این پیشفرض را دور میزنند و عمداً رم را هدر میدهند تا زمانهای اجرای قطعی و عدم تغییر اندازه در میانه عملیات را تضمین کنند.
توسعهدهندگان محدود در حافظه
در محیطهای تعبیهشده یا با تراکم بالا کار میکنند که در آنها صرفهجویی در رم بسیار حیاتیتر از جلوگیری از سربار برخورد پردازنده است.
برای توسعهدهندگانی که نرمافزارهایی برای دستگاههای اینترنت اشیا، میکروکنترلرها یا محیطهای کانتینری با تراکم بالا مینویسند، حافظه محدودیت سختتری نسبت به چرخههای پردازنده است. این مهندسان اغلب ضریب بار را به ۰٫۹ یا ۱٫۰ نزدیک میکنند. در حالی که این کار نرخ بالاتری از برخوردهای هش را تضمین میکند و پردازنده را مجبور میسازد در طول جستجوها سختتر کار کند، اما اطمینان میدهد که ردپای برنامه تا حد ممکن کوچک باقی بماند و از خرابیهای ناشی از کمبود حافظه در دستگاههایی با محدودیتهای سختافزاری شدید جلوگیری میکند.
- نگهدارندگان کتابخانه استاندارد
- اولویت دادن به پیشفرضهای ایمن و همهمنظوره که از اتمام حافظه جلوگیری کرده و در عین حال سرعت قابلقبولی را برای برنامههای متوسط حفظ میکنند.
- مهندسان تاخیر پایین
- وقفههای ناشی از هشکردن مجدد را غیرقابلقبول میدانند و ترجیح میدهند با تخصیص پیشاپیش آرایههای پراکنده با ضرایب بار پایینتر، حافظه را هدر دهند.
- توسعهدهندگان محدود در حافظه
- در محیطهای تعبیهشده یا با تراکم بالا کار میکنند که در آنها صرفهجویی در رم بسیار حیاتیتر از جلوگیری از سربار برخورد پردازنده است.
دیدگاههایی که این گزارش پوشش نداده
- طراحان کامپایلر
- معماران سختافزار
منابع
[1]Baeldungتوسعهدهندگان محدود در حافظهJava HashMap Load Factor
مطالعه در Baeldung →
[2]DEV Communityمهندسان تاخیر پایینHash Map Deep Dive
مطالعه در DEV Community →
[3]Experiments in program optimisationمهندسان تاخیر پایینChoosing the hash map's capacity
مطالعه در Experiments in program optimisation →
[4]Mediumتوسعهدهندگان محدود در حافظهBuilding a Fast, Memory-Efficient Hash Table in Java (by borrowing the best ideas)
مطالعه در Medium →
[5]Computer Science Stack Exchangeنگهدارندگان کتابخانه استانداردWhy is the Java HashMap load factor 0.75?
مطالعه در Computer Science Stack Exchange →
[6]Stack Overflowنگهدارندگان کتابخانه استانداردWhat is the significance of load factor in HashMap?
مطالعه در Stack Overflow →
[7]تیم سردبیری کوهستانتحلیل تیم سردبیری کوهستان
مطالعه در تیم سردبیری کوهستان →
بیشتر در فناوری
مشاهده همه →ایپیآی عاملها
اوپنایآی در رویداد DevDay 2026 از کُدکس ابری و ایپیآی کنترل رایانه رونمایی کرد
7 منبع
سازوکار درونی گیت
گیت چگونه کامیتها، درختها و بلابها را به عنوان اشیای محتوا-محور در یک گراف جهتدار غیرمدور ذخیره میکند
6 منبع
مقررات هوش مصنوعی
طرح قانون جدید آمریکا برای ممنوعیت هوش مصنوعی فوقهوشمند (ASI) و توقف توسعه پیشرفته آن
3 منبع
محاسبات ممیز شناور
چرا در برنامهنویسی مدرن، حاصل ۰.۱ + ۰.۲ برابر با ۰.۳ نیست؟
7 منبع
نظرات
هر زاویه. هر روز.
اخبار فناوری با پوشش کامل منابع و تحلیل دیدگاهها، هر روز و رایگان.





