حزمة parallel وdetectCores()
شغّل عناقيد fork أو socket ووزّع العمل على أنوية وحدة المعالجة المركزية
حزمة parallel وdetectCores() درس مجاني في R Academy على CoddyKit. هذا هو الدرس 1 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في R Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة R Academy 4 دروس في المجموع.
لماذا الحوسبة المتوازية؟
تحتوي الحواسيب الحديثة على عدة أنوية لوحدة المعالجة المركزية. يعمل R افتراضيًا على نواة واحدة، فتظل الأنوية الأخرى خاملة. تتيح لكم حزمة parallel (المضمنة في R) الاستفادة من جميع الأنوية لتسريع العمليات الحسابية المتكررة.
# Check how many cores your machine has
library(parallel)
total_cores <- detectCores()
logical_cores <- detectCores(logical = TRUE)
physical_cores <- detectCores(logical = FALSE)
cat('Total logical cores:', total_cores, '
')
cat('Physical cores:', physical_cores, '
')makeCluster وstopCluster
تنشئ makeCluster(n) عدد n من عمليات العاملين. استدعوا دائمًا stopCluster(cl) عند الانتهاء لتحرير الموارد. من الأساليب الشائعة استخدام detectCores() - 1 لترك نواة واحدة لنظام التشغيل.
library(parallel)
# Spawn workers (leave 1 core for system)
n_cores <- detectCores() - 1
cl <- makeCluster(n_cores)
cat('Cluster created with', n_cores, 'workers\n')
# Always clean up!
stopCluster(cl)
cat('Cluster stopped.\n')clusterExport: مشاركة المتغيرات
تمتلك عمليات العاملين مساحة ذاكرة خاصة بها ولا يمكنها رؤية البيئة العامة لديكم. استخدموا clusterExport(cl, varlist) لنسخ الكائنات المسماة من العملية الرئيسية إلى جميع العاملين.
library(parallel)
cl <- makeCluster(2)
# Define a variable and a function in the master
base_value <- 100
add_base <- function(x) x + base_value
# Export them to workers
clusterExport(cl, varlist = c('base_value', 'add_base'))
# Now workers can use them
result <- parLapply(cl, 1:4, function(x) add_base(x))
cat(unlist(result), '\n') # 101 102 103 104
stopCluster(cl)clusterEvalQ: تشغيل شفرة الإعداد
تقيّم clusterEvalQ(cl, expr) تعبيرًا على كل عامل — وهي مفيدة لتحميل الحزم أو تضمين ملفات المساعدة في جميع العقد قبل بدء الحساب الرئيسي.
library(parallel)
cl <- makeCluster(2)
# Load a package on every worker
clusterEvalQ(cl, {
library(stats)
set.seed(42) # set seed per worker
})
# Each worker can now use stats functions
result <- parLapply(cl, 1:4, function(n) rnorm(n, mean = 0, sd = 1))
result[[1]] # one random normal value
stopCluster(cl)parLapply: lapply بالتوازي
إن parLapply(cl, X, FUN) هو النظير المتوازي لـ lapply(). يوزع عناصر X على العاملين ويجمع النتائج في قائمة. ويعمل على جميع المنصات (Windows وmacOS وLinux).
library(parallel)
cl <- makeCluster(2)
# Simulate slow computation: sleep 0.1s per item
clusterEvalQ(cl, Sys.sleep)
slow_square <- function(x) {
Sys.sleep(0.05)
x^2
}
clusterExport(cl, 'slow_square')
system.time(
result <- parLapply(cl, 1:8, slow_square)
)
cat(unlist(result), '\n') # 1 4 9 16 25 36 49 64
stopCluster(cl)parSapply: تبسيط النتائج
إن parSapply(cl, X, FUN) هو الإصدار المتوازي من sapply(). ويبسط النتيجة تلقائيًا إلى متجه أو مصفوفة متى أمكن، ما يجعله مناسبًا عندما تعيد دالتكم قيمة مفردة.
library(parallel)
cl <- makeCluster(2)
# parSapply returns a simplified vector
squares <- parSapply(cl, 1:10, function(x) x^2)
cat(squares, '\n') # 1 4 9 16 25 36 49 64 81 100
# Returns a matrix if FUN returns a vector of same length
stats_result <- parSapply(cl, 1:4, function(n) {
x <- rnorm(100)
c(mean = mean(x), sd = sd(x))
})
print(stats_result) # 2x4 matrix
stopCluster(cl)mclapply: التوازي القائم على التفريع
تستخدم mclapply() أسلوب التفريع — إذ تنسخ عملية R الحالية بدلًا من إنشاء عمليات جديدة، ما يجعل بدء التشغيل أسرع ويمنح العاملين وصولًا ضمنيًا إلى بيئة العملية الأصلية. لكنها تعمل فقط على Unix وmacOS، وليس على Windows.
library(parallel)
# mclapply: Unix/macOS only
# Workers inherit the parent environment automatically
base_value <- 42
if (.Platform$OS.type != 'windows') {
result <- mclapply(
1:8,
function(x) x * base_value, # base_value visible without export
mc.cores = 4
)
cat(unlist(result), '\n')
} else {
cat('mclapply not supported on Windows. Use parLapply instead.\n')
}قياس الأداء التسلسلي مقابل المتوازي
ينطوي التوازي على تكاليف إضافية: فبدء تشغيل العنقود، وتسلسل البيانات، والاتصال بين العمليات تستغرق جميعها وقتًا. ولا يكون مفيدًا إلا عندما تكون الحسابات الخاصة بكل عنصر مكلفة بما يكفي لتجاوز هذه التكاليف.
library(parallel)
cl <- makeCluster(2)
# Task: compute 100 iterations of matrix multiply
heavy_task <- function(n) {
m <- matrix(rnorm(200), nrow = 100)
sum(m %*% t(m))
}
clusterExport(cl, 'heavy_task')
seq_time <- system.time(lapply(1:20, heavy_task))[['elapsed']]
par_time <- system.time(parLapply(cl, 1:20, heavy_task))[['elapsed']]
cat('Sequential:', round(seq_time, 3), 's\n')
cat('Parallel: ', round(par_time, 3), 's\n')
cat('Speedup: ', round(seq_time / par_time, 2), 'x\n')
stopCluster(cl)تهيئة RNG في التوازي
يُعد توليد الأرقام العشوائية بالتوازي أمرًا معقدًا — إذ يحتاج كل عامل إلى تدفق مستقل وقابل لإعادة الإنتاج. استخدموا clusterSetRNGStream(cl, seed) مع مولد L'Ecuyer-CMRG للحصول على عشوائية متوازية قابلة لإعادة الإنتاج.
library(parallel)
cl <- makeCluster(2)
# Set reproducible RNG streams across workers
clusterSetRNGStream(cl, iseed = 123)
# Each worker uses its own independent random stream
results1 <- parSapply(cl, 1:6, function(i) rnorm(1))
# Reset and repeat — same results
clusterSetRNGStream(cl, iseed = 123)
results2 <- parSapply(cl, 1:6, function(i) rnorm(1))
cat('Run 1:', round(results1, 4), '\n')
cat('Run 2:', round(results2, 4), '\n')
cat('Identical:', identical(results1, results2), '\n')
stopCluster(cl)معالجة الأخطاء في العناقيد
إذا ألقى أحد العاملين خطأً، فإن parLapply() يعيد إلقاءه في العملية الرئيسية. غلّفوا الاستدعاءات داخل الدالة باستخدام tryCatch()، أو استخدموا tryCatch() حول استدعاء parLapply() بالكامل لمعالجة حالات الفشل بسلاسة.
library(parallel)
cl <- makeCluster(2)
# Wrap risky code inside the worker function
safe_log <- function(x) {
tryCatch(
log(x),
warning = function(w) NA_real_,
error = function(e) NA_real_
)
}
clusterExport(cl, 'safe_log')
# -1 produces NaN warning, 'a' produces an error
input <- list(4, 9, -1, 'a', 16)
result <- parLapply(cl, input, safe_log)
cat(unlist(result), '\n') # 1.386 2.197 NaN NA 2.773
stopCluster(cl)نمط عملي للتوازي
إليكم سير عمل متوازي كامل واصطلاحي: اكتشاف الأنوية، وإنشاء العنقود، وتصدير التبعيات، وتشغيل الحساب، وجمع النتائج، وإيقاف العنقود دائمًا — حتى عند حدوث خطأ — باستخدام on.exit().
library(parallel)
run_parallel <- function(data, fn, n_workers = detectCores() - 1) {
cl <- makeCluster(n_workers)
on.exit(stopCluster(cl)) # guaranteed cleanup
clusterExport(cl, 'fn', envir = environment())
result <- parLapply(cl, data, fn)
result
}
# Example: bootstrap mean estimation
samples <- lapply(1:100, function(i) rnorm(50, mean = 5, sd = 2))
means <- run_parallel(samples, mean)
cat('Grand mean:', round(mean(unlist(means)), 3), '\n')
cat('95% CI: [',
round(quantile(unlist(means), 0.025), 3), ',',
round(quantile(unlist(means), 0.975), 3), ']\n')تحقق سريع
ما الدالة التي ينبغي استخدامُها لضمان استدعاء stopCluster(cl) حتى عند حدوث خطأ داخل الشفرة المتوازية؟
مراجعة: حزمة parallel
أهم النقاط:
- تعرض
detectCores()عدد الأنوية المتاحة؛ استخدمواdetectCores() - 1لحجم العنقود - تدير
makeCluster(n)/stopCluster(cl)دورة حياة العاملين - تنسخ
clusterExport()الكائنات؛ وتشغّلclusterEvalQ()شفرة الإعداد على العاملين - تُعد
parLapply()/parSapply()مكررات متوازية متعددة المنصات - تعمل
mclapply()على Unix فقط، لكنها أسرع في بدء التشغيل بفضل التفريع - استخدموا
on.exit(stopCluster(cl))لضمان التنظيف - لا يكون التوازي مفيدًا إلا للمهام كثيفة الحساب
# Minimal reproducible parallel pattern
library(parallel)
cl <- makeCluster(max(1, detectCores() - 1))
on.exit(stopCluster(cl))
clusterSetRNGStream(cl, iseed = 42)
result <- parSapply(cl, 1:8, function(x) x^2 + rnorm(1, 0, 0.1))
cat(round(result, 2), '\n')الأسئلة الشائعة
هل درس «حزمة parallel وdetectCores()» مجاني؟
نعم — نص درس «حزمة parallel وdetectCores()» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة R Academy، انتقل إلى CoddyKit PRO. تتضمن دورة R Academy 4 دروس في المجموع.
ماذا ستتعلم في «حزمة parallel وdetectCores()»؟
شغّل عناقيد fork أو socket ووزّع العمل على أنوية وحدة المعالجة المركزية تتمرن على R Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.
هل أحتاج إلى خبرة سابقة لأبدأ R Academy؟
لا تُشترط خبرة سابقة. R Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 1 من أصل 4.
كم من الوقت يستغرق درس «حزمة parallel وdetectCores()»؟
معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.
هل يمكنني كتابة وتشغيل أكواد في درس R Academy هذا؟
نعم. كل درس في R Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.
جميع الدروس في هذه الدورة
- حزمة parallel وdetectCores()
- إطار عمل future
- furrr: عمليات purrr المتوازية
- تصحيح أخطاء التعليمات البرمجية المتوازية وموازنة الأحمال