خواستم ببینم برای بالا بردن سرعت اجرای کدها در C++ چه راه ها و ترفند هایی وجود داره ؟
لطفا هر راهی و هر ترفندی برای بالا بردن سرعت بلدین بگین چون برنامه ای که دارم می نویسم به سرعت بالا خیلی نیاز داره .
خواستم ببینم برای بالا بردن سرعت اجرای کدها در C++ چه راه ها و ترفند هایی وجود داره ؟
لطفا هر راهی و هر ترفندی برای بالا بردن سرعت بلدین بگین چون برنامه ای که دارم می نویسم به سرعت بالا خیلی نیاز داره .
کد های C و ++C در صورت Optimize بودن Compiler خیلی سریع اجرا میشن ولی شما میتونی از Inline Assembly هم استفاده کنی یا با استفاده از تکنیک ها و Libهای مختلفی برای پردازش موازی هست که میتونن بهت کمک کنن .
Everything that has a beginning has an end. ... The End?
من پردازندم چند هسته ای نیست به خاطر همین نمی تونم از پردازش موازی استفاده کنم. ولی در مورد Optimize بودن Compiler و Inline Assembly میشه بهتر توضیح بدین یا مثال بزنید؟
اخه این طوری که نمیشه چیزی گفت باید کد بزاری
با چیزایی مثل موازی کردن، چند خطی، اسمبل،،Data Align ، SSE و ...
همه اینا رو سرعت تاثیر داره
بیشتر به تونایی خود برنامه نویس بر می گرده هرچی بیشتر بلد باشی کد بهتری می زنی
من مشکلم تو کد نویسی نیست فقط میخوام ترفندهایی که در مورد سرعت بلد نیست یاد بگیرم.
اگه یه مثال بزنید خوب میشه.
در مورد Inline Assembly میشه توضیح بدین ؟
استفاده از اسمبلی تو C++ میشه Inline assembly
int main()
{
__asm{
}
}
تو vC++
خوب چطوری ؟
حالا اگر استفاده کردم سرعت تقریبا چند برابر میشه ؟؟؟
دوست عزیز تاثیرش در جاهای مختلف فرق داره.
بهتره سیستمو قویتر کرد تا استفاده از این روش.
من می خوام خود نرم افزارم قوی باشه !
حالا نگفتی به طور معمول واسه یه کد ساده سرعت چند برابر میشه ؟
گفتم که تو جاها ی مختلف فرق داره.
حتی به نحوه ی نوشته شدن کدها بستگی شدیدی داره.
چه بسا شما کدتو کند تر بنویسی.و C++ سریعتر باشه.
اگر میخوای نرم افزارت قوی باشه همین C++ کافیه.
در بهترین حالت چی ؟ بابا بلاخره یه رقمی بده دیگه ؟
اخه عزیز من اینطور نیست که بگی فلان کنی سرعت انقدر بیشتر باشه
اول چیزی که می خوای رو بنویس
بعد چیزایی که مهم هستن و خیلی تکرا می شن و میشه بهینه کرد رو بشین بهینه کن
باید خیلی چیزا بلد باشی
بهترین حالتش اینه که 4برابر بشه البته من تا اینحد تونستم.(شاید بیشترم بشه کرد)
شما کد بزار ما بهینه می کنیم
حالا مگه چی میخوای بنویسی که اینقدر سرعت مهمه؟
دارم انجین شطرنج می نویسم.
اگه کامپایلر Optimize باشه چی ؟ اون موقع سرعت در بهترین حالت چند برابر میشه ؟
شما لطف کن شرح کامل برنامتو بده
بابا خیلی طولانی فقط build کردنش 10 دقیقس نمی خوام وقتتون رو بگیرم شما فقط لطف کنید چیز هایی که گفتم رو بهم یاد بدین اگه هم نمی خواید اشکالی نداره حداقل یه مقاله در این رابطه بهم معرفی کنید.
__forceinline void alwm(int gg[][11][11],int sor[11][11])
{
int af=0;
gg[0][0][0]=0;
if (sor[1][1]!=15) sor[10][3]=1;
if (sor[8][1]!=15) sor[10][4]=1;
for (int s1=1;s1<9;s1++)
for (int s2=1;s2<9;s2++)
{
if (sor[s1][s2]!=0)
{
switch (sor[s1][s2])
{
case 3:
wp(gg,s1,s2,sor,af);
break;
case 9:
wn(gg,s1,s2,sor,af);
break;
case 12:
wb(gg,s1,s2,sor,af);
break;
case 15:
wr(gg,s1,s2,sor,af);
break;
case 27:
wq(gg,s1,s2,sor,af);
break;
case 126:
wk(gg,s1,s2,sor,af);
break;
}
}
af=gg[0][0][0];
}
wlok(gg,sor,af);
af=gg[0][0][0];
wrok(gg,sor,af);
af=gg[0][0][0];
return;
}
__forceinline void albm(int gg[][11][11],int sor[11][11])
{
int af=0;
gg[0][0][0]=0;
if (sor[1][8]!=-15) sor[10][5]=1;
if (sor[8][8]!=-15) sor[10][6]=1;
for (int s1=1;s1<9;s1++)
for (int s2=1;s2<9;s2++)
{
switch (sor[s1][s2])
{
case -3:
bp(gg,s1,s2,sor,af);
break;
case -9:
bn(gg,s1,s2,sor,af);
break;
case -12:
bb(gg,s1,s2,sor,af);
break;
case -15:
br(gg,s1,s2,sor,af);
break;
case -27:
bq(gg,s1,s2,sor,af);
break;
case -126:
bk(gg,s1,s2,sor,af);
break;
}
af=gg[0][0][0];
}
blok(gg,sor,af);
af=gg[0][0][0];
brok(gg,sor,af);
af=gg[0][0][0];
return;
}
در ضمن من همه ی تابع هاشو __forceinline کردم
سلام
شاید تاکید این دوستمون روی عدد چند برابر سریعتر شدن هم پر بیراه نباشه.
بالاخره هر الگوریتم یه درجه ای داره. که نسبت به واحد معینی و سرعت پردانزده و غیره میشه با تقریب بسیار خوبی زمان رو محاسبه کرد.
برای روش موازی که پیچیدگی زمانی به راحتی قابل محاسبست. در نتیجه بهبود رو هم میشه محاسبه کرد.
برای استفاده از اسمبلی هم حتما معیاری وجود داره تا بشه داخل محاسبات پیچیدگی زمانی داخلش کرد.
کلا با دیدن ی تیکه کد نمیشه کاری کرد
باید کل برنامه باشه و خودت هم نوشته باشی و بدونی چی چقدر اجرا می شه و ..
احساس می کنم پست های منو نمی خونی
گفتم اینا رو یاد بگیر
Asembly
CallingConvention
Multi Thread
Multi Process
SIMD
Data Alignment
درضمن این جور چیزا رو هم هیچ وقت بهینه نمی کنن مگر اینکه طرف خیلی بی کار باشه
مثلا این ضرب ماتریس 4در4 هست که خودم با SSE بهینه کردم (هر گونه کپی برداری از این کد نامردیست)
void XMat4x4::Mul( const XMat4x4* a, const XMat4x4* b, XMat4x4* dst )
{
#ifndef XUSE_SSE
dst->m[0] = a->m[0] * b->m[0] + a->m[4] * b->m[1] + a->m[8] * b->m[2] + a->m[12] * b->m[3];
dst->m[1] = a->m[1] * b->m[0] + a->m[5] * b->m[1] + a->m[9] * b->m[2] + a->m[13] * b->m[3];
dst->m[2] = a->m[2] * b->m[0] + a->m[6] * b->m[1] + a->m[10] * b->m[2] + a->m[14] * b->m[3];
dst->m[3] = a->m[3] * b->m[0] + a->m[7] * b->m[1] + a->m[11] * b->m[2] + a->m[15] * b->m[3];
dst->m[4] = a->m[0] * b->m[4] + a->m[4] * b->m[5] + a->m[8] * b->m[6] + a->m[12] * b->m[7];
dst->m[5] = a->m[1] * b->m[4] + a->m[5] * b->m[5] + a->m[9] * b->m[6] + a->m[13] * b->m[7];
dst->m[6] = a->m[2] * b->m[4] + a->m[6] * b->m[5] + a->m[10] * b->m[6] + a->m[14] * b->m[7];
dst->m[7] = a->m[3] * b->m[4] + a->m[7] * b->m[5] + a->m[11] * b->m[6] + a->m[15] * b->m[7];
dst->m[8] = a->m[0] * b->m[8] + a->m[4] * b->m[9] + a->m[8] * b->m[10] + a->m[12] * b->m[11];
dst->m[9] = a->m[1] * b->m[8] + a->m[5] * b->m[9] + a->m[9] * b->m[10] + a->m[13] * b->m[11];
dst->m[10] = a->m[2] * b->m[8] + a->m[6] * b->m[9] + a->m[10] * b->m[10] + a->m[14] * b->m[11];
dst->m[11] = a->m[3] * b->m[8] + a->m[7] * b->m[9] + a->m[11] * b->m[10] + a->m[15] * b->m[11];
dst->m[12] = a->m[0] * b->m[12] + a->m[4] * b->m[13] + a->m[8] * b->m[14] + a->m[12] * b->m[15];
dst->m[13] = a->m[1] * b->m[12] + a->m[5] * b->m[13] + a->m[9] * b->m[14] + a->m[13] * b->m[15];
dst->m[14] = a->m[2] * b->m[12] + a->m[6] * b->m[13] + a->m[10] * b->m[14] + a->m[14] * b->m[15];
dst->m[15] = a->m[3] * b->m[12] + a->m[7] * b->m[13] + a->m[11] * b->m[14] + a->m[15] * b->m[15];
#else
__m128 xmmC1 = _mm_load_ps(a->m);
__m128 xmmC2 = _mm_load_ps(a->m + 4);
__m128 xmmC3 = _mm_load_ps(a->m + 8);
__m128 xmmC4 = _mm_load_ps(a->m + 12);
__m128 xmmR1 = _mm_load_ps(b->m);
__m128 xmmR2 = _mm_load_ps(b->m+4);
_mm_store_ps(dst->m, _mm_add_ps(_mm_add_ps(_mm_add_ps(
_mm_mul_ps(xmmC1, _mm_allw_ps(xmmR1))
,_mm_mul_ps(xmmC2, _mm_allx_ps(xmmR1)))
,_mm_mul_ps(xmmC3, _mm_ally_ps(xmmR1)))
,_mm_mul_ps(xmmC4, _mm_allz_ps(xmmR1))));
_mm_store_ps(dst->m+4, _mm_add_ps(_mm_add_ps(_mm_add_ps(
_mm_mul_ps(xmmC1, _mm_allw_ps(xmmR2))
,_mm_mul_ps(xmmC2, _mm_allx_ps(xmmR2)))
,_mm_mul_ps(xmmC3, _mm_ally_ps(xmmR2)))
,_mm_mul_ps(xmmC4, _mm_allz_ps(xmmR2))));
xmmR1 = _mm_load_ps(b->m+8);
xmmR2 = _mm_load_ps(b->m+12);
_mm_store_ps(dst->m+8, _mm_add_ps(_mm_add_ps(_mm_add_ps(
_mm_mul_ps(xmmC1, _mm_allw_ps(xmmR1))
,_mm_mul_ps(xmmC2, _mm_allx_ps(xmmR1)))
,_mm_mul_ps(xmmC3, _mm_ally_ps(xmmR1)))
,_mm_mul_ps(xmmC4, _mm_allz_ps(xmmR1))));
_mm_store_ps(dst->m+12, _mm_add_ps(_mm_add_ps(_mm_add_ps(
_mm_mul_ps(xmmC1, _mm_allw_ps(xmmR2))
,_mm_mul_ps(xmmC2, _mm_allx_ps(xmmR2)))
,_mm_mul_ps(xmmC3, _mm_ally_ps(xmmR2)))
,_mm_mul_ps(xmmC4, _mm_allz_ps(xmmR2))));
#endif
}
شما این همه branch زائد ایجاد کردید که چی ؟
static const int lut1[127] ={0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,1};
if (lut1[sor[s1][s2]])
wp(gg,s1,s2,sor,af);
این کدی که قرار دادم یکی از تکنیک های branch pridection هستش تابع دوم را به همین ترتیب تغییر بدید.
موفق باشید.
آقای mostafa.sataki شما تو کدی که گذاشتین فقط تابع wp() بکار رفته په بقیه ی تابع ها چی ؟ اونا کجا رفتن ؟
خوب من اینو فهمیدم ولی بلاخره به جوابم نرسیدم که شما چرا در کدتون فقط تابع wp رو بکار بردین در صورتی که اگر نگاه کنید تو دستور switch تابع های دیگه ای مثل wn ، wr ، wk و ... رو بکار بردم !!!
اره اینم فکر خوبیه ولی عدد ها منفیه
تو کد شما اگه عدد مثبت باشه درسته.
این طوری درست تر کار می کنه
static const int lut1[127] ={0,0,0,1,0,0,0,0,0,1,0,0,1,0,0,1,0,0,0,0,0,0,0,0, 0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,1};
const int* lutEnd = lut1 + 127;
if (lutEnd[sor[s1][s2]])
wp(gg,s1,s2,sor,af);
اینم ی مثال ساده
عوض این
switch(n)
{
case 1 :
func();
break;
case 3 :
func();
break;
case 4 :
func();
break;
case 5 :
func();
break;
case 6 :
func();
break;
case 8 :
func();
break;
}
این کارو می کنی
const int nums[] = {0, 1, 0, 1, 1, 1, 1, 0, 1 };
if(nums[n])
func();
بابا اگه یه کمی دقت کنین می بینین که این کده با قسمت switch کدی که من دادم یکی نیست چون تو اون کد تابع هایی مثل wr wn wk wb و ... بکار رفته نه فقط تابع wp .نمی دونم چرا به این دقت نمی کنین.
اه اره راس می گی
خب تقصیر خودته چرا اسم تابع ها رو این طوری می زاری ادم گیج میشه دیگه
wp,wl,wr که نشد اسم
ولی به هر حال ترفند جالبی بود دستت درد نکنه
اگر برنامت محاسبات تکراری زیاد داره میتونی اونارو با constexpr (مخصوص C++11 ) زمان کامپایل حساب کنی یک جا ذخیرشون کنی بعد تو برنامه ازشون استفاده کنی
واین که این کدی که شما گزاشتی سرعتش از این بیشتر نمی شه! بهتر بود یکی از فانکشن های اصلی رو میزاشتی مثل wp...
سعی کن آرایه هات رو ازنوع اشاره گر تعریف کنی
این به خاطر اینه که بتونی memory رو مدیریت کنی
از توابعی که <memory> بهت میده استفاده کن که بتونی اشاره گرها رو پاک کنی
چون هر چی متغیرها و آرایه هات کمتر باشن حافظه کمتری مصرف میشه و در نتیجه سرعت برنامه به شدت میره بالاتر
و اینکه برنامت رو به صورت Multi-threading بنویس که بتونه از تمام CPU استفاده کنه
حتما برنامه رو strip و optimize کن
این موجب این میشه که اولا سرعت برنامه بره بالاتر و در ثانی اینکه حجم برنامه کم بشه
از SSE استفاده کن تا سرعت برنامه بره بالاتر
اگه از MinGW استفاده میکنی از سویچ -mthreads استفاده کن که کامپایلرم یه جورایی تو multi-threading کمکت کنه
و اینکه سعی کن تعداد دستوراتی که مینویسی کمتر باشه تا سرعت بره بالاتر
سعی کن حلقه های for که استفاده میکنی به جای اضافه کردن یه واحد به شمارنده در هر تکرار چند واحد اضافه کنی.
این روش رو تو یه مقاله خوندم. در ضمن کتابخانه lapack که مربوط به توابع جبر خطی میشه و توابعش فوق العاده بهینه هست هم تو توابعش از این روش استفاده کرده.
یه مثال ساده میارم تا بهتر متوجه شی :
این روش معمولیه که همه استفاده میکنن :
for(int i=0;i<10000;i++) x[i]=0;
این روش بهینه تره :
for(int i=0;i<10000;i+=4)
{
x[i]=0;
x[i+1]=0;
x[i+2]=0;
x[i+3]=0;
}
تو سوالی که ایشون پرسیدند مشکل جای دیگری بود.
این تکنیک که شما عرض کردید تحت عنوان تکنیک unroll هستش و اکثر کامپایلر های خانوده C++ خودشان اینکار را به صورت خودکار انجام می دهند به این علت هستش که از miss cache در cache l2 جلوگیری میشه. در ضمن در موارد خاص هم شما می تونید کامپایلر را force کنید تا حلقه مورد نظر را به تعداد دلخواه شما unroll نماید برای unroll دستی از (progma unroll(n# استفاده نمایید.
بابا هیچ کی نیست جواب بده ؟ ما این جا علاف شدیم
برای Multi thread می تونید از کتابخانه TBB استفاده کنید که فوق العاده هستش البته از C++11 هم می تونید برای اینکار استفاده کنید.
strip هم در مود release vs خودش حذف می شه در gcc فکر کنم دقیق نمی دونم باید خودتون اینکارو انجام بدید.
توی sse هم شما با ریجسترهای 128 بیتی کار می کنید خلاصه کلام بجای اینکه چهار بار جمع 32 بیتی انجام بدید یکبار fetch می کنید هر 4 تا32 بیتی رو به یکباره جمع می کنید تو زمینه های داده های متوالی وجود داشته باشه مثل پردازش روی آرایه و تصویر کاربرد زیادی دازه و سرعت رو هم تا حدی افزایش می ده و برای اینکار بایستی چک کنید سیستم مقصد چه sse رو ساپورت می کنه .
MinGW همون gcc تحت ویندوز هستش.
خوب چند روز اینجا نیومدم
یه آشنایی با multi-threading
وقتی شما نرم افزار رو به صورت multi-thread مینویسی به این معنیه که میگی از تمام رجیسترهای سی پی یو استفاده کن
حالا نرم افزار من مگه از multi-threading استفاده نمیکنه؟
جواب کاملا در حالت عادی منفیه
نرم افزار شما توی یه قسمت سی پی یو ران میشه و در نتیجه شما نمیتونی از سرعت بیشتری بهره مند بشی
خوب چه جوری کل سی پی یو رو کنترل کنم؟
با توابعی که تو <thread> و <mutex> بهت میده میتونی این کارو بکنی
یه قسمت سی پی یو رو لاک کنی تا نرم افزار ازش استفاده کنه و موقعی که تایم داده شده توسط شما تموم شد خودش آنلاک بشه و یا تا زمان خارج شدن برنامه لاک بمونه
خوب با این حساب میخوایم ببینیم چه جوری لاک میشه
یه سری کتابخونه مثل Tiny Thread++ و یا tbb و یا boost.thread هست که شما میتونی به صورت multi-threading برنامه بنویسی
اما سویچ -mthreads
این سویچ به شما کمک میکنه برنامه رو از طریق multi-threading کامپایل کنی که سی پی یو رو غیر از اون چیزی که نوشتی به صورت بهینه شده کنترل کنه
اما تو MinGW چجوری strip کنم
بعد از کامپایل کردن سورس به فایل آبجکت موقع لینک سویچ -s رو بهش میدین و لینک میکنین
sse چیه؟
یه سری دستوره که کامپایلر ازش برا لینکینگ استفاده میکنه که سرعت رو هم تا حدودی افزایش میده
MinGW چیه؟
یه سری نرم افزار لینوکسی مثل GCC, GDB, make و غیره هستش که تو سیستم عاملهای GNU وجود داره
سوال بود در خدمتم
بابا من هرچی می خوام پروژه مو کامپایل کنم دکمه کامپایل غیر فعاله ! دلیلش چیه ؟ دواش چیه ؟