التوجيهية لتحسين السرعة
استبدل الحلقات الصريحة بعمليات توجيهية لتحقيق تحسينات كبيرة في السرعة
التوجيهية لتحسين السرعة درس مجاني في R Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في R Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة R Academy 4 دروس في المجموع.
أهمية التوجيهية
لغة R مفسَّرة، ولذلك تحمل حلقات for تكلفة إضافية في كل تكرار — مثل توزيع استدعاءات الدوال، والتحقق من الحدود، وتحويل الأنواع. أما العمليات المتجهة فتنقل هذا العمل إلى شيفرة C المترجمة، التي تعمل بسرعة أكبر بمراتب عديدة.
تُعد التوجيهية أهم تحسين متاح في R الأساسي.
مثال على الحلقة مقابل cumsum()
يوضح حساب المجموع التراكمي باستخدام حلقة for مقارنةً بالدالة المدمجة cumsum() الفرق بوضوح. تستدعي cumsum() شيفرة مترجمة على مستوى C وتعالج المتجه بأكمله في مرور واحد.
n <- 500000
x <- rnorm(n)
t_loop <- system.time({
result <- numeric(n)
result[1] <- x[1]
for (i in 2:n) result[i] <- result[i-1] + x[i]
})['elapsed']
t_vec <- system.time({
result2 <- cumsum(x)
})['elapsed']
cat('Loop :', t_loop, 's
')
cat('cumsum:', t_vec, 's
')ifelse() مقابل for + if
تُعد ifelse(condition, yes, no) تعليمة شرطية متجهة تقيّم الشرط على متجه كامل دفعة واحدة. وهي تستبدل حلقات for + if التي تعالج العناصر واحدًا تلو الآخر بمرور واحد على مستوى C.
n <- 300000
x <- rnorm(n)
t_loop <- system.time({
result <- numeric(n)
for (i in seq_len(n)) result[i] <- if (x[i] > 0) x[i] else -x[i]
})['elapsed']
t_vec <- system.time({
result2 <- ifelse(x > 0, x, -x)
})['elapsed']
cat('for+if :', t_loop, 's
')
cat('ifelse :', t_vec, 's
')التخصيص المسبق لمتجهات النتائج
عندما تكون الحلقة ضرورية، خصّص مسبقًا متجه النتيجة قبل الحلقة. إذ يؤدي تكبير المتجه باستخدام c(result, new_val) داخل حلقة إلى نسخ المتجه بأكمله في كل تكرار — أي ما مجموعه O(n^2) من عمليات الذاكرة.
n <- 20000
t_grow <- system.time({
result <- c()
for (i in seq_len(n)) result <- c(result, i^2)
})['elapsed']
t_prealloc <- system.time({
result2 <- numeric(n)
for (i in seq_len(n)) result2[i] <- i^2
})['elapsed']
cat('Growing vector:', t_grow, 's
')
cat('Pre-allocated :', t_prealloc, 's
')الأنواع الصحيحة للتخصيص المسبق
استخدم المُنشئ ذي النوع المطابق لبياناتك لتجنب التحويل الضمني أثناء التخصيص المسبق:
numeric(n)— أعداد عشرية بدقة مزدوجةinteger(n)— أعداد صحيحةcharacter(n)— سلاسل نصية فارغةlogical(n)— قيم FALSEvector('list', n)— قائمة من قيم NULL
n <- 5
cat('numeric :', numeric(n), '
')
cat('integer :', integer(n), '
')
cat('logical :', logical(n), '
')
cat('character:', character(n), '
')
cat('list len :', length(vector('list', n)), '
')colSums() وrowSums() مقابل apply()
بالنسبة إلى عمليات المصفوفات، تُعد colSums(m) وrowSums(m) وcolMeans(m) وrowMeans(m) إجراءات C محسّنة بدرجة كبيرة. وهي أسرع باستمرار من apply(m, 1, sum)، التي تستدعي دالة R sum مرة واحدة لكل صف.
m <- matrix(rnorm(1000 * 2000), nrow = 1000)
t_apply <- system.time(apply(m, 2, sum))['elapsed']
t_colsums <- system.time(colSums(m))['elapsed']
cat('apply(m,2,sum):', t_apply, 's
')
cat('colSums(m) :', t_colsums, 's
')الحساب المتجهي سريع دائمًا
جميع العمليات الحسابية الأساسية على المتجهات — + و- و* و/ و^ وsqrt() وlog() وexp() — متجهة. فهي تعمل عنصرًا بعنصر على متجه كامل ضمن استدعاء C واحد. فضّلها دائمًا على الحلقات.
x <- 1:1000000
t1 <- system.time(y <- x^2 + 2*x + 1)['elapsed']
t2 <- system.time({
y2 <- numeric(length(x))
for (i in seq_along(x)) y2[i] <- x[i]^2 + 2*x[i] + 1
})['elapsed']
cat('Vectorized:', t1, 's
')
cat('Loop :', t2, 's
')التقسيم المنطقي بدلًا من الحلقات
تصفية متجه باستخدام شرط منطقي هي عملية متجهة. بدلًا من إجراء حلقة وإضافة العناصر وفقًا لشرط، أنشئ فهرسًا منطقيًا ونفّذ التقسيم مرة واحدة — إذ ينفّذ كود C الأساسي مرورًا واحدًا.
x <- rnorm(500000)
t_loop <- system.time({
pos <- c()
for (v in x) if (v > 0) pos <- c(pos, v)
})['elapsed']
t_vec <- system.time({
pos2 <- x[x > 0]
})['elapsed']
cat('Loop filter:', t_loop, 's
')
cat('Logical idx:', t_vec, 's
')استخدام which() وtabulate() للعمل مع الفهارس
عندما تحتاج إلى مواضع القيم TRUE، تكون which(condition) عملية متجهة وسريعة. وتحصي tabulate(bin_vector) تكرارات الأعداد الصحيحة بسرعة أكبر من table() عند التعامل مع نطاقات صحيحة كثيفة.
x <- sample(1:10, 100000, replace = TRUE)
t_table <- system.time(table(x))['elapsed']
t_tabulate <- system.time(tabulate(x, nbins = 10))['elapsed']
cat('table() :', t_table, 's
')
cat('tabulate():', t_tabulate, 's
')
idx <- which(x == 5)
cat('Positions of 5: first 5 =', head(idx, 5), '
')متى تكون الحلقات مقبولة
ليست كل الحلقات سيئة. تكون الحلقات مقبولة عندما:
- يعتمد كل تكرار على النتيجة السابقة (اعتمادية تسلسلية)
- يكون عدد التكرارات صغيرًا (< 1000)
- يستدعي جسم الحلقة دالة معقدة لا يوجد لها بديل متجه
في هذه الحالات، ركّز على الحجز المسبق للذاكرة وتجنّب توسيع البنى داخل الحلقة.
# Sequential dependency -- loop is correct here
fib <- function(n) {
result <- integer(n)
result[1] <- 1L
if (n >= 2) result[2] <- 1L
for (i in seq_len(n)[-c(1,2)]) result[i] <- result[i-1] + result[i-2]
result
}
cat('Fibonacci:', fib(10), '
')ملخص العمليات المتجهة
القواعد الأساسية للعمليات المتجهة لكتابة كود R سريع:
- استخدم
cumsum/cumprod/diffللتجميع التسلسلي - استخدم
ifelse()للشروط المطبقة على العناصر - احجز الذاكرة مسبقًا باستخدام
numeric(n)/vector('list',n) - استخدم
colSums/rowSums/colMeans/rowMeansبدلًا منapply() - التقسيم المنطقي أفضل من حلقات التصفية
اختبار سريع: الحجز المسبق للذاكرة
لماذا يكون توسيع متجه باستخدام result <- c(result, new_val) داخل حلقة بطيئًا جدًا عندما تكون قيمة n كبيرة؟
مراجعة العمليات المتجهة
العمليات المتجهة هي الوسيلة الأساسية لتحسين أداء R:
- تستدعي الدوال المتجهة (
cumsumوifelseوالعوامل الحسابية) كود C المترجم — ولذلك فهي أسرع من حلقات R المكافئة بمقدار يتراوح بين 10 و100 مرة - احجز حاويات النتائج مسبقًا قبل أي حلقة لا يمكن تجنّبها لتفادي النسخ ذي التعقيد O(n^2)
- تتفوق
colSums/rowSumsعلىapply()في تجميعات المصفوفات - يستبدل التقسيم المنطقي حلقات التصفية بطريقة واضحة وسريعة
الأسئلة الشائعة
هل درس «التوجيهية لتحسين السرعة» مجاني؟
نعم — نص درس «التوجيهية لتحسين السرعة» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة R Academy، انتقل إلى CoddyKit PRO. تتضمن دورة R Academy 4 دروس في المجموع.
ماذا ستتعلم في «التوجيهية لتحسين السرعة»؟
استبدل الحلقات الصريحة بعمليات توجيهية لتحقيق تحسينات كبيرة في السرعة تتمرن على R Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ R Academy؟
لا تُشترط خبرة سابقة. R Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.
كم من الوقت يستغرق درس «التوجيهية لتحسين السرعة»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس R Academy هذا؟
نعم. كل درس في R Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- system.time() وproc.time()
- تحليل أداء التعليمات البرمجية باستخدام Rprof وprofvis
- التوجيهية لتحسين السرعة
- قياس الأداء باستخدام microbenchmark