چرا توابع inline سریع تر هستن ؟ - هفت خط کد انجمن پرسش و پاسخ برنامه نویسی

چرا توابع inline سریع تر هستن ؟

+5 امتیاز

من میخواستم بدونم ما وقتی یک تابع رو بصورت inline تعریف می کنیم  یا زمان هایی که کامپایلر این کار رو خودکار انجام میده چرا تابع  سریع تر اجرا میشه؟

inline void func()

چه فرقی بین کد اسمبلی که کامپایلر برای تابع inline و تابع غیر inline تولید می کنه وجود داره که باعث سریع تر اجرا شدن کد میشه ؟

 و این که آیا inline کردن  تاثیری در حجم برنامه هم داره ؟

سوال شده مرداد 17, 1393  بوسیله ی Xavi (امتیاز 627)   24 83 110
دوباره تگ گذاری شد مرداد 17, 1393 بوسیله ی BlueBlade
اگه میخواین تابعی رو inline کنید حواستون باشه که کد کمی در تابع وجود داشته باشه و تابع زیاد سنگین نباشه
اگه با زمان کامپایل شدن مشکلی ندارید می‌تونید همه تابع‌ها رو inline کنید: http://stackoverflow.com/questions/3999806/why-not-mark-everything-inline
کامپیالرهای برجسته مشکلی ندارن باهاش ضمن این که مشکلات linker کم می‌شه.
من متوجه نمیشم چرا inline کردن همه توابع مشکلات لینکر رو کم می کنه ؟!
می‌شه دو تابع اینلاین هم نام در دو فایل فایل مختلف تعریف کرد. اگر اینلاین نباشن نمی‌شه.

2 پاسخ

+6 امتیاز
 
بهترین پاسخ

وقتی که تابع inline باشه کامپایلر مستقیم کد داخل تابع رو داخل main برنامه میزاره اگرتابع  inline نباشه از call  برای صدا زدن procedure استفاده میشه .

در تابعی که inline شده علاوه بر این که عملیات push  و pop انجام نمیشه یک دلیل دیگه هم داره که سریع تر اجرا میشه وقتی دستور call داخل اسمبلی استفاده میشه cpu مجبور میشه که صبر کنه تا دستور اجرا بشه بعد بقیه دستورات رو اجرا کنه جالا اگر مثلا مقدار بازگشتی از تابع داخل یک  دستور شرطی استفاده شده بود اگر call نباشه می تونه با استفاده از branch prediction بیاد نتیجه دستورات شرطی تابع  رو پیش بینی کنه و قسمت های بعدی کد رو هم همزمان با تابع  اجرا کنه که تاثیر خیلی زیادی داره(این پیش بینی cpu ها  بیشتر از 98% اوقات درسته ) .

inline کردن توابع حجم برنامه رو هم زیاد می کنه پس کامپایلر فقط می تونه توابعی که طول کوتاهی دارن و دفعات زیادی هم استفاده نمیشن رو inline کنه چون اگر بخواد توابع بزرگتر رو inline کنه حجم کد تولیدی به شدت زیاد میشه .

مثلا این کد رو در نظر بگیرید :

#include <iostream>

int foo(int val)
{
	if (val < 50)
		return 1;
	else
		return 0;
}
int main()
{
	int r = 0;
	for (int i = 0; i < 100;i++){
		r += foo(i);
	}
	if (r>10)
		r -= 5;
	std::cout << r;
}

کد زیر کد اسمبلی هستش  که ویژوال استودیو در حالت releaseو تنظیمات مربوط به حداکثر سرعت  تولید می کنه (البته این قسمت مرتبط بود چند هزار خط هم مربوط به cout بود که نزاشتم)

_TEXT	SEGMENT
_main	PROC						; COMDAT
; Line 12
	xor	edx, edx
; Line 13
	xor	eax, eax
$LL4@main:
; Line 5
	xor	ecx, ecx
	cmp	eax, 50					; 00000032H
	setl	cl
; Line 13
	inc	eax
; Line 14
	add	edx, ecx
	cmp	eax, 100				; 00000064H
	jl	SHORT $LL4@main
; Line 16
	cmp	edx, 10					; 0000000aH
	jle	SHORT $LN1@main
; Line 17
	sub	edx, 5
$LN1@main:
; Line 18
	mov	ecx, DWORD PTR __imp_?cout@std@@3V?$basic_ostream@DU?$char_traits@D@std@@@1@A
	push	edx
	call	DWORD PTR __imp_??6?$basic_ostream@DU?$char_traits@D@std@@@std@@QAEAAV01@H@Z
; Line 19
	xor	eax, eax
	ret	0
_main	ENDP
_TEXT	ENDS

 

داخل کد بالا تابع foo چون کوتاه بوده با این که inline تعریف نشده

کامپایلر   بصورت خودکار inline کرده و داخل main گداشته   (  line5 تا line16  داخل کد اسمبلی) و چون این جا call نداریم cpu از branch prediction استفاده می کنه وif (r>10 رو هم همزمان decode می کنه .

ولی تابع cout چون پیجیده تر بوده inline نشده  و از call برای صدا زدن استفاده شده .

پاسخ داده شده مرداد 17, 1393 بوسیله ی BlueBlade (امتیاز 15,315)   15 18 89
انتخاب شد مرداد 27, 1393 بوسیله ی Xavi
میشه دقیق تر بگید چطوری کد اسمبلی مربوط به کد C++ را در ویژوال استدیو به دست آوردید؟
کلید های Alt + F7 رو بزنید property پروژه باز میشه از قسمت ++C/C منو چپ Output files رو انتخاب کنید بعد داخل  Assembler Output گزینه Assembly With Source Code (/FAs) رو انتخاب کنید.
حالا یک بار دیگه پروژه رو build کنید یک فایل با اسم پروژه و پسوند .asm داخل پوشه release پروژه ایجاد میشه که کد اسمبلی تولیدی هست
البته یک راه دیگه هم هست یک break point داخل کد بزارید (روی نوار کمرنگ سمت چپ کپ کلیک کنید یا روی خط مورد نظر کلیک راست کنید Break point--> Insert Break point رو انتخاب کنید)
بعد با F5 پروژه رو در حالت دیباگ بیلد کنید .
حالا  از قسمت debug---Windows ->Disassembly رو انتخاب کنید داخل پنجره ای که باز میشه  کد اسمبلی در حال اجرا قابل دیدنه .
بابت هر دو روش ممنون :-)
+5 امتیاز

سلام!

به طور کلی اگه تابع شما inline نباشه، در کد Assembly عملیات PUSH و POP برای قراردادن و دریافت داده ها از Stack انجام می شه که زمان گیر هست...

برای ورود به تابع، داده های مورد نیاز تابع در Stack قرار می گیرن و بعد از ورود به تابع، تابع داده ها رو از Stack دریافت می کنه... البته این روش ساده ی این کار هست... Compiler ها معمولا برای بهینه سازی سعی می کنن تعدادی از آرگومان ها رو به کمک Register ها به تابع تحویل بدن که سرعت کار بالاتر بره... برای خروج از تابع هم ممکنه از Stack یا در شرایط بهینه از یه Register استفاده بشه...

اما با وجود سرعت بیش تر توابع inline در عوض مشکلاتی هم براشون وجود داره... مثلا زیاد شدن حجم کد... یا این که توابع بازگشتی با ساختار Stack به راحتی پیاده سازی می شن، اما برای یه تابع بازگشتی inline ترفندهایی رو باید به کار برد یا اصلا ممکنه Compiler به صورت inline تعریفش نکنه... Compiler ممکنه inline بودن تابع شما رو در نظر نگیره و تابع بازگشتی شما رو به صورت عادی به کمک Stack پیاده سازی کنه چون این که inline باشه یا نه صرفا توصیه ی شما به Compiler هست... به هر حال بعضی Compiler ها ممکنه توابع بازگشتی inline شما رو به شکل عجیب و غریبی تولید کنن! یعنی بیان تا n مرحله از تابع بازگشتی inline شما رو کد معادلش رو تولید کنن... که واقعا تابعتون بازگشتی نمی شه...! به این می گن inline recursion... اما در کل به دلیل محدودیت هایی که در پیاده سازی توابع به صورت inline هست، ممکنه Compiler از inline پیاده سازی کردن این جور توابع صرف نظر کنه...

پاسخ داده شده مرداد 17, 1393 بوسیله ی مسعود لپه‌چی (امتیاز 928)   12 31 50
ویرایش شده مرداد 17, 1393 بوسیله ی مسعود لپه‌چی
...