چرا Numpy سریعتره و کندوکاو در اعماق چند دستور

یک آرایه نامپای در واقع با ترکیب metadata تعریف میشه (مثلا تعداد ابعاد، shape، نوع داده) و خود داده‌ها. داده‌ها تو بلاکهای پیوسته و همسان مموری (RAM) ذخیره میشن. تفاوت اساسی بین یه آرایه و یه لیست نامپای در واقع همین پیوسته بودن داده‌ها در مموریه. همین تفاوته که باعث میشه نامپای اینقدر سریعتر و بهینه‌تر باشه.

چرا این موضوع مهمه؟ چند تا از دلیلاش رو اینجا می‌نویسم:

  • محاسبات روی آرایه میتونن خیلی بهینه با زبان سطح پایین C نوشته شن ( عملا بیشتر نامپای با C نوشته شده). با دونستن آدرس بلاک مموری و نوع دیتا (Data Type) محاسبات تبدیل به یه حلقه ساده روی تمام آیتم‌ها میشه. در صورتی که همین محاسبات با پایتون کلی overhead داره (چون باید آدرس هر عضو لیست جداگونه به دست بیاد).
  • همسایگی داده‌ها در حافظه باعث میشه که بشه از کش سی‌پی‌یو استفاده کرد. اینطوری که سی‌پی‌یو بخشهایی از حافظه که داده توشه رو تو رجیستر خودش کش میکنه و اینجوری سرعت محاسبات به شدت بالا میره.
  • همسایگی حافظه باعث میشه نامپای بتونه از دستورات محاسبات برداری‌ای که در سی‌پی‌یوهای جدیدتر هست استفاده کنه. مثلا تکنولوژی SSE و AVX تو اینتل و XOP تو AMD.

در ادامه کاری که میخوایم بکنیم اینه که تو چند دستور ساده ناپای عمیق‌تر بشیم و از نظر استفاده از حافظه و همینطور سرعت محاسبه بررسیشون کنیم.

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

 

import numpy as np
def aid(x):
    # This function returns the memory
    # block address of an array.
    return x.__array_interface__['data'][0]

اگر دو آرایه به یک نقطه از حافظه اشاره کنند، تابع aid برای این دو آرایه مقادیر یکسانی برمی‌گردونه. خب، حالا بریم سراغ دستورات پایتون.

۱. گاهی وقتها ما میخوایم یک آرایه پایتون رو عینا کپی کنیم. برای مثلا میخوایم آرایه اصلی رو دست نخورده نگه داریم و محاسبات رو روی آرای جدید انجام بدیم:

 

import numpy as np
a = np.zeros(10)
ax = aid(a)
ax
۳۲۲۵۰۱۱۲
b = a.copy()
aid(b) == ax
False

۲. روی آرایه‌ها دو نوع محاسبات میشه انجام داد. محاسبات in-place که همون آرایه اصلی تغییر میکنه و محاسبات implicit-copy که یک کپی جدید از آرایه برگردونده میشه:

a *= 2
aid(a) == ax
True
c = a * 2
aid(c) == ax
False

محاسبات implicit-copy نسبت به in-placeها خیلی کندترند:

%%timeit a = np.zeros(10000000)
a *= 2
۴٫۸۵ ms ± ۲۴ µs per loop (mean ± std. dev. of 7 runs,
۱۰۰ loops each)
%%timeit a = np.zeros(10000000)
b = a * 2
۷٫۷ ms ± ۱۰۵ µs per loop (mean ± std. dev. of 7 runs,
۱۰۰ loops each)

۳. تغییر shape یک ممکنه نیاز به کپی کردن آرایه داشته باشه و ممکنه هم نداشته باشه. برای مثلا reshape یک ماتریس دو بعدی نیازی به کپی نداره، مگر اینکه قبلش ترنسپوز کرده باشیمش!

 

a = np.zeros((100, 100))
ax = aid(a)
b = a.reshape((1, -1))
aid(b) == ax
True
c = a.T.reshape((1, -1))
aid(c) == ax
False

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

 

%timeit b = a.reshape((1, -1))
۳۳۰ ns ± ۰٫۵۱۷ ns per loop (mean ± std. dev. of 7 runs
    ۱۰۰۰۰۰۰ loops each)
%timeit a.T.reshape((1, -1))
۵ µs ± ۵٫۶۸ ns per loop (mean ± std. dev. of 7 runs,
    ۱۰۰۰۰۰ loops each)

۴.هر دو تابع flatten و ravel آرایه رو تبدیل به یک آرایه یک بعدی (فلت) می‌کنند. با این حال، flatten همیشه یک کپی برمی‌گردونه و ravel فقط زمانی کپی برمی‌گردونه که لازم باشه (برای همین معمولا سریعتره).

 

d = a.flatten()
aid(d) == ax
False
e = a.ravel()
aid(e) == ax
True
%timeit a.flatten()
۲٫۳ µs ± ۱۸٫۱ ns per loop (mean ± std. dev. of 7 runs,
۱۰۰۰۰۰ loops each)
%timeit a.ravel()
۱۹۹ ns ± ۵٫۰۲ ns per loop (mean ± std. dev. of 7 runs,
۱۰۰۰۰۰۰۰ loops each)

دیدگاهتان را بنویسید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

android application Google I/O HomeBrew ImageMagick Material Design mobile PogressBar RecyclerView splash TEDxKish ux آموزش اندروید الوین تافلر اندروید اپلیکیشن برنامه نویسی برنامه نویسی اندروید برنامه‌نویسی برنامه‌نویسی اندروید تداکس کیش تعمیر تغییر سایز عکس با ترمینال خلاصه کتاب دانش داده دانشگاه دانشگاه ایده‌آل دزد دیتا ساینس رشته مهندسی کامپیوتر ریکامندرسیستم سیستم‌های توصیه‌گر فری‌لنس لپتاپ متریال دیزاین معرفی کتاب موج سوم نوار پیشرفت همایش هوش مصنوعی ُجزیره کیش کار کسب و کار یادگیری عمیق یادگیری ماشین