"* প্রয়োগ" পরিবার কি আসলে ভেক্টরাইজড নয়?


138

সুতরাং আমরা প্রত্যেক আর নতুন ব্যবহারকারীকে বলার জন্য ব্যবহৃত হয়েছি যে " applyভেক্টরাইজড নয়, প্যাট্রিক বার্নস আর ইনফার্নো সার্কেল 4 " যা বলে (আমি উদ্ধৃতি দিয়েছি) দেখুন:

একটি সাধারণ প্রতিচ্ছবি হ'ল প্রয়োগ পরিবারে একটি ফাংশন ব্যবহার করা। এটি ভেক্টরাইজেশন নয় , এটি লুপ-লুকানো । প্রয়োগ ফাংশনটির সংজ্ঞাটিতে লুপ রয়েছে। ল্যাপলি ফাংশনটি লুপটিকে সমাহিত করে, তবে মৃত্যুদন্ড কার্যকর করার সময়গুলি লুপের জন্য একটি স্পষ্টতার সমান হতে পারে।

প্রকৃতপক্ষে, applyউত্স কোডটিতে একটি তাত্ক্ষণিক দৃষ্টিভঙ্গি লুপটি প্রকাশ করে:

grep("for", capture.output(getAnywhere("apply")), value = TRUE)
## [1] "        for (i in 1L:d2) {"  "    else for (i in 1L:d2) {"

এখন পর্যন্ত ঠিক আছে, তবে একটি চেহারা lapplyবা vapplyআসলে একটি সম্পূর্ণ আলাদা চিত্র প্রকাশ করে:

lapply
## function (X, FUN, ...) 
## {
##     FUN <- match.fun(FUN)
##     if (!is.vector(X) || is.object(X)) 
##        X <- as.list(X)
##     .Internal(lapply(X, FUN))
## }
## <bytecode: 0x000000000284b618>
## <environment: namespace:base>

সুতরাং দৃশ্যত সেখানে কোনও আর forলুপ লুকিয়ে নেই, বরং তারা অভ্যন্তরীণ সি লিখিত ফাংশনটি কল করছে।

খরগোশের গর্তের একটি তাত্ক্ষণিক দৃষ্টিভঙ্গি একই চিত্রটি প্রকাশ করে

তদুপরি, colMeansউদাহরণস্বরূপ ফাংশনটি গ্রহণ করা যাক , যা কখনও ভেক্টরাইজড না হওয়ার জন্য অভিযুক্ত ছিল না

colMeans
# function (x, na.rm = FALSE, dims = 1L) 
# {
#   if (is.data.frame(x)) 
#     x <- as.matrix(x)
#   if (!is.array(x) || length(dn <- dim(x)) < 2L) 
#     stop("'x' must be an array of at least two dimensions")
#   if (dims < 1L || dims > length(dn) - 1L) 
#     stop("invalid 'dims'")
#   n <- prod(dn[1L:dims])
#   dn <- dn[-(1L:dims)]
#   z <- if (is.complex(x)) 
#     .Internal(colMeans(Re(x), n, prod(dn), na.rm)) + (0+1i) * 
#     .Internal(colMeans(Im(x), n, prod(dn), na.rm))
#   else .Internal(colMeans(x, n, prod(dn), na.rm))
#   if (length(dn) > 1L) {
#     dim(z) <- dn
#     dimnames(z) <- dimnames(x)[-(1L:dims)]
#   }
#   else names(z) <- dimnames(x)[[dims + 1]]
#   z
# }
# <bytecode: 0x0000000008f89d20>
#   <environment: namespace:base>

তাই না? এটি কেবল কল দেয় .Internal(colMeans(...যা আমরা খরগোশের গর্তেও খুঁজে পেতে পারি । তাহলে এটি কীভাবে আলাদা .Internal(lapply(..?

প্রকৃতপক্ষে একটি দ্রুত মানদণ্ড প্রকাশ করে যে একটি বড় ডেটা সেটের জন্য লুপের চেয়ে sapplyখারাপ আর বেশি কিছু করে নাcolMeansfor

m <- as.data.frame(matrix(1:1e7, ncol = 1e5))
system.time(colMeans(m))
# user  system elapsed 
# 1.69    0.03    1.73 
system.time(sapply(m, mean))
# user  system elapsed 
# 1.50    0.03    1.60 
system.time(apply(m, 2, mean))
# user  system elapsed 
# 3.84    0.03    3.90 
system.time(for(i in 1:ncol(m)) mean(m[, i]))
# user  system elapsed 
# 13.78    0.01   13.93 

অন্য কথায়, এটি কি সঠিক lapplyএবং vapply সত্যই ভেক্টরাইজড (এটি তুলনামূলকভাবে applyএকটি forলুপ যা তুলনা করে lapply) এবং প্যাট্রিক বার্নস আসলে কী বলতে চেয়েছিল?


8
এগুলি সব শব্দার্থবিজ্ঞানের মধ্যে রয়েছে তবে আমি সেগুলি ভেক্টরাইজড হিসাবে বিবেচনা করব না। আমি যদি কোনও আর ফাংশনটি কেবল একবার কল করা হয় এবং মানগুলির একটি ভেক্টর পাস করতে পারি তবে আমি ভেক্টরাইজড একটি পদ্ধতির বিবেচনা করি। *applyফাংশন বার বার আর ফাংশন কল করে, যা তাদের লুপ করে তোলে। এর ভাল পারফরম্যান্স সম্পর্কে sapply(m, mean): সম্ভবত lapplyপদ্ধতিটির সি-কোড কেবল একবার প্রেরণ করে এবং বার বার পদ্ধতিটি কল করে? mean.defaultবেশ আশাবাদী।
রোল্যান্ড

4
দুর্দান্ত প্রশ্ন এবং অন্তর্নিহিত কোডটি পরীক্ষা করার জন্য ধন্যবাদ thanks আমি এটি সম্প্রতি পরিবর্তন করা হয়েছে কিনা তা দেখছিলাম, তবে সংস্করণ 2.13.0 থেকে আর রিলিজ নোটগুলিতে কিছুই নেই।
ইলির

1
পারফরম্যান্সটি প্ল্যাটফর্ম এবং সি-সংকলক এবং লিংক পতাকা উভয় ক্ষেত্রেই নির্ভর করে?
স্মি

3
@ ডেভিড আরেনবুর্গ আসলে, আমি মনে করি এটি ভাল সংজ্ঞাযুক্ত নয়। কমপক্ষে আমি কোনও আধ্যাত্মিক রেফারেন্স জানি না। ভাষার সংজ্ঞা "ভেক্টরাইজড" ক্রিয়াকলাপ উল্লেখ করে, তবে ভেক্টরাইজেশন সংজ্ঞায়িত করে না।
রোল্যান্ড

3
খুব সম্পর্কিত: আর এর পরিবার কী সিনট্যাকটিক চিনির চেয়ে বেশি প্রয়োগ করে? (এবং এই উত্তরগুলির মতো এটিও একটি ভাল পঠিত))
গ্রেগর টমাস

উত্তর:


73

প্রথম সব, আপনার উদাহরণে আপনি একটি "data.frame" যার জন্য ঠিক না উপর পরীক্ষা করা colMeans, applyএবং "[.data.frame"তারা একটি ওভারহেড আছে যেহেতু:

system.time(as.matrix(m))  #called by `colMeans` and `apply`
#   user  system elapsed 
#   1.03    0.00    1.05
system.time(for(i in 1:ncol(m)) m[, i])  #in the `for` loop
#   user  system elapsed 
#  12.93    0.01   13.07

একটি ম্যাট্রিক্সে, ছবিটি কিছুটা আলাদা:

mm = as.matrix(m)
system.time(colMeans(mm))
#   user  system elapsed 
#   0.01    0.00    0.01 
system.time(apply(mm, 2, mean))
#   user  system elapsed 
#   1.48    0.03    1.53 
system.time(for(i in 1:ncol(mm)) mean(mm[, i]))
#   user  system elapsed 
#   1.22    0.00    1.21

প্রশ্নের মূল অংশটি নিবন্ধন করে lapply/ mapply/ ইত্যাদি এবং সোজা আর-লুপের মধ্যে প্রধান পার্থক্যটি যেখানে লুপিং করা হয়। রোল্যান্ড নোট হিসাবে, উভয় সি এবং আর লুপগুলিকে প্রতিটি পুনরাবৃত্তিতে একটি আর ফাংশন মূল্যায়ন করতে হবে যা সবচেয়ে ব্যয়বহুল। সত্যিকারের দ্রুত সি ফাংশনগুলি সেগুলি যা সিতে সমস্ত কিছু করে, সুতরাং, আমার ধারণা, এটি "ভেক্টরাইজড" সম্পর্কে যা হওয়া উচিত?

একটি উদাহরণ যেখানে আমরা "তালিকা" এর প্রতিটি উপাদানের মধ্যস্থতা খুঁজে পাই:

( ১১ ই মে '16 সম্পাদনা করুন : আমি বিশ্বাস করি যে "সংখ্যার" উপর আর এর গড় অ্যালগরিদমের বিশেষত্বের কারণে, "আর্ট" অনুসন্ধানের উদাহরণটি কোনও আর ফাংশনকে পুনরাবৃত্তভাবে এবং সংকলিত কোডের মূল্যায়নের মধ্যে পার্থক্যের জন্য ভাল সেটআপ নয়) একটি সরল sum(x) / length(x)ও (2) এর সাথে "তালিকা" এর পরীক্ষা করার জন্য এটি আরও বোধ করা উচিত length(x) >> lengths(x)So

একটি সাধারণ উদাহরণ হিসাবে আমরা length == 1একটি "তালিকার" প্রতিটি উপাদানগুলির বিপরীত সন্ধানের বিষয়টি বিবেচনা করতে পারি :

একটি tmp.cফাইলে:

#include <R.h>
#define USE_RINTERNALS 
#include <Rinternals.h>
#include <Rdefines.h>

/* call a C function inside another */
double oppC(double x) { return(ISNAN(x) ? NA_REAL : -x); }
SEXP sapply_oppC(SEXP x)
{
    SEXP ans = PROTECT(allocVector(REALSXP, LENGTH(x)));
    for(int i = 0; i < LENGTH(x); i++) 
        REAL(ans)[i] = oppC(REAL(VECTOR_ELT(x, i))[0]);

    UNPROTECT(1);
    return(ans);
}

/* call an R function inside a C function;
 * will be used with 'f' as a closure and as a builtin */    
SEXP sapply_oppR(SEXP x, SEXP f)
{
    SEXP call = PROTECT(allocVector(LANGSXP, 2));
    SETCAR(call, install(CHAR(STRING_ELT(f, 0))));

    SEXP ans = PROTECT(allocVector(REALSXP, LENGTH(x)));     
    for(int i = 0; i < LENGTH(x); i++) { 
        SETCADR(call, VECTOR_ELT(x, i));
        REAL(ans)[i] = REAL(eval(call, R_GlobalEnv))[0];
    }

    UNPROTECT(2);
    return(ans);
}

এবং আর দিকে:

system("R CMD SHLIB /home/~/tmp.c")
dyn.load("/home/~/tmp.so")

তথ্য সহ:

set.seed(007)
myls = rep_len(as.list(c(NA, runif(3))), 1e7)

#a closure wrapper of `-`
oppR = function(x) -x

for_oppR = compiler::cmpfun(function(x, f)
{
    f = match.fun(f)  
    ans = numeric(length(x))
    for(i in seq_along(x)) ans[[i]] = f(x[[i]])
    return(ans)
})

মাপকাঠিতে:

#call a C function iteratively
system.time({ sapplyC =  .Call("sapply_oppC", myls) }) 
#   user  system elapsed 
#  0.048   0.000   0.047 

#evaluate an R closure iteratively
system.time({ sapplyRC =  .Call("sapply_oppR", myls, "oppR") }) 
#   user  system elapsed 
#  3.348   0.000   3.358 

#evaluate an R builtin iteratively
system.time({ sapplyRCprim =  .Call("sapply_oppR", myls, "-") }) 
#   user  system elapsed 
#  0.652   0.000   0.653 

#loop with a R closure
system.time({ forR = for_oppR(myls, "oppR") })
#   user  system elapsed 
#  4.396   0.000   4.409 

#loop with an R builtin
system.time({ forRprim = for_oppR(myls, "-") })
#   user  system elapsed 
#  1.908   0.000   1.913 

#for reference and testing 
system.time({ sapplyR = unlist(lapply(myls, oppR)) })
#   user  system elapsed 
#  7.080   0.068   7.170 
system.time({ sapplyRprim = unlist(lapply(myls, `-`)) }) 
#   user  system elapsed 
#  3.524   0.064   3.598 

all.equal(sapplyR, sapplyRprim)
#[1] TRUE 
all.equal(sapplyR, sapplyC)
#[1] TRUE
all.equal(sapplyR, sapplyRC)
#[1] TRUE
all.equal(sapplyR, sapplyRCprim)
#[1] TRUE
all.equal(sapplyR, forR)
#[1] TRUE
all.equal(sapplyR, forRprim)
#[1] TRUE

(গড় অনুসন্ধানের মূল উদাহরণ অনুসরণ করে):

#all computations in C
all_C = inline::cfunction(sig = c(R_ls = "list"), body = '
    SEXP tmp, ans;
    PROTECT(ans = allocVector(REALSXP, LENGTH(R_ls)));

    double *ptmp, *pans = REAL(ans);

    for(int i = 0; i < LENGTH(R_ls); i++) {
        pans[i] = 0.0;

        PROTECT(tmp = coerceVector(VECTOR_ELT(R_ls, i), REALSXP));
        ptmp = REAL(tmp);

        for(int j = 0; j < LENGTH(tmp); j++) pans[i] += ptmp[j];

        pans[i] /= LENGTH(tmp);

        UNPROTECT(1);
    }

    UNPROTECT(1);
    return(ans);
')

#a very simple `lapply(x, mean)`
C_and_R = inline::cfunction(sig = c(R_ls = "list"), body = '
    SEXP call, ans, ret;

    PROTECT(call = allocList(2));
    SET_TYPEOF(call, LANGSXP);
    SETCAR(call, install("mean"));

    PROTECT(ans = allocVector(VECSXP, LENGTH(R_ls)));
    PROTECT(ret = allocVector(REALSXP, LENGTH(ans)));

    for(int i = 0; i < LENGTH(R_ls); i++) {
        SETCADR(call, VECTOR_ELT(R_ls, i));
        SET_VECTOR_ELT(ans, i, eval(call, R_GlobalEnv));
    }

    double *pret = REAL(ret);
    for(int i = 0; i < LENGTH(ans); i++) pret[i] = REAL(VECTOR_ELT(ans, i))[0];

    UNPROTECT(3);
    return(ret);
')                    

R_lapply = function(x) unlist(lapply(x, mean))                       

R_loop = function(x) 
{
    ans = numeric(length(x))
    for(i in seq_along(x)) ans[i] = mean(x[[i]])
    return(ans)
} 

R_loopcmp = compiler::cmpfun(R_loop)


set.seed(007); myls = replicate(1e4, runif(1e3), simplify = FALSE)
all.equal(all_C(myls), C_and_R(myls))
#[1] TRUE
all.equal(all_C(myls), R_lapply(myls))
#[1] TRUE
all.equal(all_C(myls), R_loop(myls))
#[1] TRUE
all.equal(all_C(myls), R_loopcmp(myls))
#[1] TRUE

microbenchmark::microbenchmark(all_C(myls), 
                               C_and_R(myls), 
                               R_lapply(myls), 
                               R_loop(myls), 
                               R_loopcmp(myls), 
                               times = 15)
#Unit: milliseconds
#            expr       min        lq    median        uq      max neval
#     all_C(myls)  37.29183  38.19107  38.69359  39.58083  41.3861    15
#   C_and_R(myls) 117.21457 123.22044 124.58148 130.85513 169.6822    15
#  R_lapply(myls)  98.48009 103.80717 106.55519 109.54890 116.3150    15
#    R_loop(myls) 122.40367 130.85061 132.61378 138.53664 178.5128    15
# R_loopcmp(myls) 105.63228 111.38340 112.16781 115.68909 128.1976    15

10
ডেটা.ফ্রেমকে ম্যাট্রিক্সে রূপান্তর করার ব্যয় সম্পর্কে দুর্দান্ত পয়েন্ট এবং মানদণ্ড সরবরাহ করার জন্য ধন্যবাদ।
জোশুয়া উলিরিচ

এটি একটি খুব সুন্দর উত্তর, যদিও আমি আপনার all_Cএবং C_and_Rফাংশনগুলি সংকলন করতে সক্ষম ছিলাম না । আমি ল্যাপলিরcompiler::cmpfun একটি পুরানো আর সংস্করণের ডকুমেন্টেশনেও পেয়েছি যার মধ্যে একটি আসল আর forলুপ রয়েছে, আমি সন্দেহ করতে শুরু করি যে বার্নস সেই পুরানো সংস্করণটির কথা উল্লেখ করছেন যা তখন থেকেই ভেক্টরাইজড ছিল এবং এটিই আমার প্রশ্নের আসল উত্তর ... ..
ডেভিড আরেনবার্গ

@DavidArenburg: মাপকাঠিতে la1থেকে ?compiler::cmpfunমনে হয়, এখনও সব কিন্তু সাথে একই দক্ষতা উত্পাদ all_Cফাংশন। আমার ধারণা, এটি-সংজ্ঞা- একটি সংজ্ঞা হিসাবে আসে; "ভেক্টরাইজড" অর্থ এমন কোনও ফাংশন যা কেবল স্কেলারকেই গ্রহণ করে না, সি কোড রয়েছে এমন কোনও ফাংশন, কেবল সিটিতে গণনা ব্যবহার করে এমন কোনও ফাংশন?
অ্যালেক্সিস_লাজ

1
আমি সব দ ফাংশন, তাদের মধ্যে সি কোড আছে কারণ সবকিছু আর একটি ফাংশন (যা কিছু ভাষায় লেখা করা হয়েছিল) হয়। সুতরাং মূলত, যদি আমি এটি সঠিকভাবে বুঝতে পারি, আপনি বলছেন যে lapplyএটি ভেক্টরাইজড নয় কেবল কারণ এটি প্রতিটি সিট কোডের সাথে প্রতিটি পুনরাবৃত্তির একটি আর ফাংশন মূল্যায়ন করছে ?
ডেভিড আরেনবুর্গ

5
@ ডেভিড আরেনবুর্গ: আমার যদি কোনওভাবে "ভেক্টরাইজেশন" সংজ্ঞায়িত করতে হয়, আমার ধারণা, আমি একটি ভাষাগত পদ্ধতি বেছে নেব; অর্থাত্ একটি ফাংশন যা গ্রহণ করে এবং জানে যে কীভাবে "ভেক্টর" পরিচালনা করতে হয়, এটি দ্রুত, ধীর, সি তে লেখা, আর বা অন্য যে কোনও কিছুই নয়। আর-তে ভেক্টরাইজেশনের গুরুত্বটি হ'ল অনেকগুলি ফাংশন সি-তে লেখা হয় এবং ভেক্টরগুলিকে পরিচালনা করে থাকে অন্য ভাষাগুলিতে ব্যবহারকারীরা সাধারণত ইনপুটটি লুপটি লগ করতে পারে - গড়টি খুঁজে পান। যা গতি, দক্ষতা, সুরক্ষা এবং দৃust়তার সাথে পরোক্ষভাবে সম্পর্কিত হতে ভেক্টরাইজেশনকে তৈরি করে।
অ্যালেক্সিস_লাজ

65

আমার কাছে, ভেক্টরাইজেশনটি মূলত আপনার কোডটি লেখার জন্য আরও সহজ এবং সহজ করে বোঝার বিষয়ে।

একটি ভেক্টরাইজড ক্রিয়াকলাপের লক্ষ্য হ'ল একটি ফর লুপের সাথে যুক্ত বই-রক্ষণকে সরিয়ে ফেলা। উদাহরণস্বরূপ, পরিবর্তে:

means <- numeric(length(mtcars))
for (i in seq_along(mtcars)) {
  means[i] <- mean(mtcars[[i]])
}
sds <- numeric(length(mtcars))
for (i in seq_along(mtcars)) {
  sds[i] <- sd(mtcars[[i]])
}

তুমি লিখতে পারো:

means <- vapply(mtcars, mean, numeric(1))
sds   <- vapply(mtcars, sd, numeric(1))

এটি একই (ইনপুট ডেটা) কী এবং কী আলাদা (আপনি যে ফাংশনটি প্রয়োগ করছেন) তা দেখতে সহজ করে তোলে।

ভেক্টরাইজেশনের একটি দ্বিতীয় সুবিধা হ'ল ফর্ম-লুপটি প্রায়শই সি-তে লেখা হয় আর এর চেয়ে বেশি This এটির পারফরম্যান্স সুবিধাগুলি রয়েছে তবে আমার মনে হয় না এটি ভেক্টরাইজেশনের মূল সম্পত্তি। ভেক্টরাইজেশন মূলত আপনার মস্তিষ্ককে বাঁচানোর বিষয়ে, কম্পিউটারের কাজটি না সঞ্চয় করার বিষয়ে।


5
আমি মনে করি না যে সি এবং আর forলুপগুলির মধ্যে অর্থপূর্ণ পারফরম্যান্সের পার্থক্য রয়েছে । ঠিক আছে, একটি সি লুপটি সংকলক দ্বারা অনুকূলিত হতে পারে, তবে পারফরম্যান্সের মূল বিষয় হ'ল লুপের সামগ্রীটি দক্ষ কিনা। এবং স্পষ্টতই সংকলিত কোডটি ব্যাখ্যা করা কোডের চেয়ে দ্রুত হয়। তবে সম্ভবত এটিই আপনি বলতে চেয়েছিলেন।
রোল্যান্ড

3
@ রোল্যান্ড হ্যাঁ, এটি প্রতি সেফ নিজেই লুপ নয়, এটি চারপাশের সমস্ত জিনিস (একটি ফাংশন কলের দাম, জায়গায় পরিবর্তন করার ক্ষমতা, ...)।
হ্যাডলি

10
@ ডেভিড আরেনবুর্গ "অযথা অবিচ্ছিন্নতা হ'ল ছোট মনের হাবগোব্লিন";)
হ্যাডলি

6
না, আমি মনে করি না পারফরম্যান্সই আপনার কোডটিকে ভেক্টরাইজিংয়ের মূল বিষয়। একটি লুপে একটি লুপটি পুনরায় লেখালেখি এটি দ্রুততর না হলেও উপকারী। Dplyr এর মূল বিষয়টি হ'ল ডেটা ম্যানিপুলেশন প্রকাশ করা সহজ করে তোলে (এবং এটি খুব দ্রুত যে এটি দ্রুতও)।
হ্যাডলি

12
@ ডেভিড আরেনবুর্গ এটি কারণ আপনি একজন অভিজ্ঞ আর ব্যবহারকারী। বেশিরভাগ নতুন ব্যবহারকারী লুপগুলি অনেক বেশি প্রাকৃতিক খুঁজে পান এবং ভেক্টরাইজ করতে উত্সাহিত হওয়া প্রয়োজন। আমার কাছে, কলমিন্সের মতো একটি ফাংশন ব্যবহার করা ভেক্টরিরিজেশন সম্পর্কে অগত্যা নয়, এটি দ্রুত কোডটি পুনরায় ব্যবহার করার বিষয়ে যা ইতিমধ্যে কেউ লিখেছেন
হ্যাডলি

49

আমি প্যাট্রিক বার্নসের এই দৃষ্টিভঙ্গির সাথে একমত যে এটি লুপ লুকানো নয় কোড ভেক্টরাইজেশন । কারণটা এখানে:

এই Cকোড স্নিপেট বিবেচনা করুন :

for (int i=0; i<n; i++)
  c[i] = a[i] + b[i]

আমরা কী করতে চাই তা বেশ পরিষ্কার। তবে কীভাবে টাস্কটি সম্পাদিত হয় বা কীভাবে এটি সম্পাদন করা যায় তা সত্য নয়। একজন জন্য-লুপ ডিফল্টরূপে একটি সিরিয়াল কনস্ট্রাক্ট হয়। সমান্তরালভাবে কীভাবে জিনিসগুলি করা যায় তা তা জানায় না।

সর্বাধিক সুস্পষ্ট উপায় হ'ল কোডটি অনুক্রমিক পদ্ধতিতে চালিত হয় । রেজিস্টারগুলি লোড করুন a[i]এবং b[i]চালিয়ে যান, এগুলি যুক্ত করুন, ফলাফলটি সংরক্ষণ করুন এবং এটির c[i]জন্য প্রতিটি করুন i

তবে, আধুনিক প্রসেসরের ভেক্টর বা সিমডি ইন্সট্রাকশন সেট রয়েছে যা একই নির্দেশনা চলাকালীন একই নির্দেশের সময় ডেটা ভেক্টরের উপর পরিচালিত করতে সক্ষম (যেমন, উপরে বর্ণিত দুটি ভেক্টর যুক্ত করা)। প্রসেসর / আর্কিটেকচারের উপর নির্ভর করে, তা থেকে, যোগ বলছি, চার নম্বরে সম্ভব হতে পারে এবং একটি সময়ে এক একই নির্দেশ অধীনে পরিবর্তে।ab

আমরা একক নির্দেশিকার একাধিক ডেটা কাজে লাগাতে এবং ডেটা স্তরের সমান্তরালতা সম্পাদন করতে চাই , অর্থাত্, একবারে 4 টি জিনিস লোড করা, এক সাথে 4 টি জিনিস যুক্ত করা, এক সাথে 4 টি জিনিস সঞ্চয় করা। এবং এটি কোড ভেক্টরাইজেশন

নোট করুন যে এটি কোড সমান্তরালতা থেকে পৃথক - যেখানে একাধিক গণনা একসাথে সঞ্চালিত হয়।

এটি দুর্দান্ত লাগবে যদি সংকলক কোডের এই জাতীয় ব্লক সনাক্ত করে এবং স্বয়ংক্রিয়ভাবে সেগুলি ভেক্টরাইজ করে, যা একটি কঠিন কাজ। কম্পিউটার সায়েন্সে স্বয়ংক্রিয় কোড ভেক্টরাইজেশন একটি চ্যালেঞ্জিং গবেষণা বিষয় topic তবে সময়ের সাথে সাথে সংকলকরা এতে আরও ভাল। আপনি এখানে অটো ভেক্টরাইজেশন ক্ষমতা পরীক্ষা করতে পারেন । একইভাবে এখানে । এবং আপনি এর তুলনায় শেষ লিঙ্কে এবং (ইন্টেল সি ++ সংকলক) কিছু মানদণ্ড খুঁজে পেতে পারেন ।GNU-gcc LLVM-clang gccICC

gcc(আমি চালু v4.9) উদাহরণস্বরূপ -O2স্তরের অপ্টিমাইজেশনে কোডটি ভেক্টরাইজ করে না । সুতরাং আমরা যদি উপরে বর্ণিত কোডটি কার্যকর করি তবে এটি ক্রমান্বয়ে চালানো হবে। 500 মিলিয়ন দৈর্ঘ্যের দুটি পূর্ণসংখ্যার ভেক্টর যুক্ত করার সময় এখানে।

আমাদের হয় পতাকা যুক্ত করতে হবে -ftree-vectorizeবা স্তরকে অপ্টিমাইজেশন পরিবর্তন করতে হবে -O3। (নোট যা অন্যান্য অতিরিক্ত অপটিমাইজেশনও-O3 সম্পাদন করে )। পতাকাটি কার্যকর যেমনটি যখন লুপটি সফলভাবে ভেক্টরাইজ করা হয়েছিল তখন অবহিত করা হয়)।-fopt-info-vec

# compiling with -O2, -ftree-vectorize and  -fopt-info-vec
# test.c:32:5: note: loop vectorized
# test.c:32:5: note: loop versioned for vectorization because of possible aliasing
# test.c:32:5: note: loop peeled for vectorization to enhance alignment    

এটি আমাদের জানায় যে ফাংশনটি ভেক্টরাইজড। 500 মিলিয়ন দৈর্ঘ্যের পূর্ণসংখ্যার ভেক্টরগুলিতে অ-ভেক্টরাইজড এবং ভেক্টরাইজড সংস্করণ উভয়ের সাথে তুলনা করার সময় এখানে রয়েছে:

x = sample(100L, 500e6L, TRUE)
y = sample(100L, 500e6L, TRUE)
z = vector("integer", 500e6L) # result vector

# non-vectorised, -O2
system.time(.Call("Csum", x, y, z))
#    user  system elapsed 
#   1.830   0.009   1.852

# vectorised using flags shown above at -O2
system.time(.Call("Csum", x, y, z))
#    user  system elapsed 
#   0.361   0.001   0.362

# both results are checked for identicalness, returns TRUE

এই অংশটি ধারাবাহিকতা না হারিয়ে নিরাপদে এড়ানো যেতে পারে।

কম্পাইলারদের সবসময় ভেক্টরাইজ করার জন্য পর্যাপ্ত তথ্য থাকবে না। আমরা সমান্তরাল প্রোগ্রামিংয়ের জন্য ওপেনএমপি স্পেসিফিকেশন ব্যবহার করতে পারি , যা কোডটি ভেক্টরাইজ করার জন্য কম্পাইলারদের নির্দেশ দেওয়ার জন্য একটি সিমড সংকলক নির্দেশও সরবরাহ করে। মেমোরির ওভারল্যাপগুলি, জাতি শর্তাদি ইত্যাদি নেই তা নিশ্চিত করা অপরিহার্য when যখন ম্যানুয়ালি কোডটি ভেক্টরাইজিং করা হয়, অন্যথায় এটির ফলস্বরূপ ভুল ফলাফল আসবে।

#pragma omp simd
for (i=0; i<n; i++) 
  c[i] = a[i] + b[i]

এটি করে, আমরা বিশেষত সংকলকটিকে এটি ভেক্টরাইজ করতে বলি না কেন তা যাই হোক না কেন। সংকলনের সময় পতাকা ব্যবহার করে আমাদের ওপেনএমপি এক্সটেনশনগুলি সক্রিয় করতে হবে -fopenmp। এটি করে:

# timing with -O2 + OpenMP with simd
x = sample(100L, 500e6L, TRUE)
y = sample(100L, 500e6L, TRUE)
z = vector("integer", 500e6L) # result vector
system.time(.Call("Cvecsum", x, y, z))
#    user  system elapsed 
#   0.360   0.001   0.360

যা মহান! এটি gcc v6.2.0 এবং llvm clang v3.9.0 (উভয় homebrew, MacOS 10.12.3 এর মাধ্যমে ইনস্টল করা) দিয়ে পরীক্ষা করা হয়েছিল, উভয়ই ওপেনএমপি ৪.০ সমর্থন করে।


এই অর্থে, যদিও অ্যারে প্রোগ্রামিংয়ের উইকিপিডিয়া পৃষ্ঠায় উল্লেখ করা হয়েছে যে সমস্ত ভাষা যেগুলি সম্পূর্ণ অ্যারেতে পরিচালিত হয় সাধারণত এটিকে ভেক্টরাইজড অপারেশন হিসাবে ডাকে , এটি সত্যই লুপ লুকানো আইএমও (এটি প্রকৃতপক্ষে ভেক্টরাইজড না হলে) হয়।

আর এর ক্ষেত্রে, সি rowSums()বা colSums()কোড কোড ভেক্টরিকরণ IIUC ব্যবহার করে না ; এটা ঠিক সি একটি লুপ একই জন্য যায় lapply()। ক্ষেত্রে apply(), এটি আর। এগুলি সমস্ত লুপ লুকানো

সংক্ষেপে, একটি আর ফাংশন মোড়ানো দ্বারা:

শুধু একটি লেখা জন্য-লুপ মধ্যে C! = আপনার কোড vectorising।
শুধু একটি লেখা জন্য-লুপ মধ্যে R! = আপনার কোড vectorising।

উদাহরণস্বরূপ ইন্টেল ম্যাথ কার্নেল লাইব্রেরি (এমকেএল) ভেক্টরিযুক্ত ফাংশনগুলি প্রয়োগ করে।

আছে HTH


তথ্যসূত্র:

  1. জেমস রিইন্ডারস দ্বারা কথা বলা, ইন্টেল (এই উত্তরটি বেশিরভাগ ক্ষেত্রে এই দুর্দান্ত আলাপের সংক্ষিপ্ত করার চেষ্টা)

35

সুতরাং দুর্দান্ত উত্তর / মন্তব্যগুলি কয়েকটি সাধারণ উত্তরের সমষ্টি করতে এবং কিছু ব্যাকগ্রাউন্ড সরবরাহ করতে: আর এর 4 ধরণের লুপ রয়েছে ( ভেক্টরাইজড থেকে ভেক্টরাইজড ক্রম পর্যন্ত নয় )

  1. আর forলুপ যা বারবার প্রতিটি পুনরাবৃত্তিতে আর ফাংশন কল করে ( ভেক্টরাইজড নয় )
  2. সি লুপ যা বারবার প্রতিটি পুনরাবৃত্তিতে আর ফাংশনকে কল করে ( ভেক্টরাইজড নয় )
  3. সি লুপ যা কেবল একবারে ফাংশনকে কল করে ( কিছুটা ভেক্টরাইজড )
  4. একটি সরল সি লুপ যা কোনও আর ফাংশনকে একেবারেই কল করে না এবং এটি নিজস্ব সংকলিত ফাংশন ব্যবহার করে ( ভেক্টরাইজড )

সুতরাং *applyপরিবারটি দ্বিতীয় ধরণের। applyপ্রথম ধরণের আরও যা বাদে

আপনি এর উত্স কোডে মন্তব্য থেকে এটি বুঝতে পারেন

/ *। অভ্যন্তরীণ (ল্যাপলি (এক্স, ফুন)) * /

/ * এটি একটি বিশেষ। অভ্যন্তরীণ, তাই অমূল্য যুক্তি রয়েছে। এটি
ক্লোজার র‌্যাপার থেকে বলা হয়, তাই এক্স এবং ফুন প্রতিশ্রুতি। উদাহরণস্বরূপ bquote ব্যবহারের জন্য মজাদার অবশ্যই অবমূল্যায়ন করতে হবে। * /

এর অর্থ হ'ল lapplyসি সি কোড আর এর থেকে একটি অমূল্য ফাংশন গ্রহণ করে এবং পরে এটি সি কোডের মধ্যেই মূল্যায়ন করে। এটি মূলত lapplyএস .Internalকলের মধ্যে পার্থক্য

.Internal(lapply(X, FUN))

যার একটি FUNযুক্তি রয়েছে যা একটি আর ফাংশন ধারণ করে

আর colMeans .Internalকল যা নেই একটি আছে FUNযুক্তি

.Internal(colMeans(Re(x), n, prod(dn), na.rm))

colMeans, অসদৃশ lapplyজানে ঠিক কি কাজ এটি ব্যবহার করার প্রয়োজন হয়, সুতরাং এটা মানে অভ্যন্তরীণভাবে সি কোড মধ্যে হিসাব করে।

আপনি সি কোডের মধ্যে প্রতিটি পুনরাবৃত্তিতে আর ফাংশনের মূল্যায়ন প্রক্রিয়াটি স্পষ্ট দেখতে পাচ্ছেনlapply

 for(R_xlen_t i = 0; i < n; i++) {
      if (realIndx) REAL(ind)[0] = (double)(i + 1);
      else INTEGER(ind)[0] = (int)(i + 1);
      tmp = eval(R_fcall, rho);   // <----------------------------- here it is
      if (MAYBE_REFERENCED(tmp)) tmp = lazy_duplicate(tmp);
      SET_VECTOR_ELT(ans, i, tmp);
   }

জিনিসগুলির সংক্ষিপ্তসার হিসাবে, lapplyভেক্টরাইজড নয় , যদিও এটি প্লেইন আর আর forলুপের দুটি সম্ভাব্য সুবিধা রয়েছে

  1. লুপে অ্যাক্সেস এবং lapplyকার্যনির্বাহী দ্রুতগতিতে মনে হয় সিতে (যেমন কোনও ফাংশনটি আনতে) তবুও পার্থক্যটি বড় মনে হলেও আমরা এখনও মাইক্রোসেকেন্ড স্তরে থাকি এবং ব্যয়বহুল জিনিসটি প্রতিটি পুনরাবৃত্তির একটি আর ফাংশনের মূল্যায়ন। একটি সহজ উদাহরণ:

    ffR = function(x)  {
        ans = vector("list", length(x))
        for(i in seq_along(x)) ans[[i]] = x[[i]]
        ans 
    }
    
    ffC = inline::cfunction(sig = c(R_x = "data.frame"), body = '
        SEXP ans;
        PROTECT(ans = allocVector(VECSXP, LENGTH(R_x)));
        for(int i = 0; i < LENGTH(R_x); i++) 
               SET_VECTOR_ELT(ans, i, VECTOR_ELT(R_x, i));
        UNPROTECT(1);
        return(ans); 
    ')
    
    set.seed(007) 
    myls = replicate(1e3, runif(1e3), simplify = FALSE)     
    mydf = as.data.frame(myls)
    
    all.equal(ffR(myls), ffC(myls))
    #[1] TRUE 
    all.equal(ffR(mydf), ffC(mydf))
    #[1] TRUE
    
    microbenchmark::microbenchmark(ffR(myls), ffC(myls), 
                                   ffR(mydf), ffC(mydf),
                                   times = 30)
    #Unit: microseconds
    #      expr       min        lq    median        uq       max neval
    # ffR(myls)  3933.764  3975.076  4073.540  5121.045 32956.580    30
    # ffC(myls)    12.553    12.934    16.695    18.210    19.481    30
    # ffR(mydf) 14799.340 15095.677 15661.889 16129.689 18439.908    30
    # ffC(mydf)    12.599    13.068    15.835    18.402    20.509    30
  2. @ রোল্যান্ড দ্বারা উল্লিখিত হিসাবে, এটি একটি সংকলিত সি লুপ চালিয়ে বরং একটি ব্যাখ্যা করা আর লুপ চালায়


যদিও আপনার কোডটি ভেক্টরাইজ করার সময় কিছু জিনিস আপনার বিবেচনায় নেওয়া দরকার।

  1. আপনার ডেটা সেট (চলো এটা কল তাহলে df) ক্লাসের হয় data.frame, কিছু ভেক্টরকৃত ফাংশন (যেমন colMeans, colSums, rowSums, ইত্যাদি) কারণ তারা কিভাবে ডিজাইন করা হয়েছে, একটি ম্যাট্রিক্স থেকে প্রতি রূপান্তর এটি প্রথম হবে। এর অর্থ এটি dfএকটি বৃহত্তর জন্য এটি একটি বিশাল ওভারহেড তৈরি করতে পারে। যদিও lapplyএটি করতে হবে না কারণ এটি প্রকৃত ভেক্টরগুলিকে বের করে দেয় df(যেমন data.frameকেবল ভেক্টরগুলির একটি তালিকা রয়েছে) এবং এইভাবে, আপনার যদি এতগুলি কলাম না থাকলেও অনেকগুলি সারি থাকে তবে lapply(df, mean)কখনও কখনও এর চেয়ে ভাল বিকল্প হতে পারে colMeans(df)
  2. আর একটি জিনিস মনে রাখবেন যে আর এর বিভিন্ন ধরণের বিভিন্ন ধরণের ফাংশন রয়েছে যেমন .Primitive, এবং জেনেরিক ( S3, S4) কিছু অতিরিক্ত তথ্যের জন্য এখানে দেখুন । জেনেরিক ফাংশনটিতে একটি পদ্ধতি প্রেরণ করতে হয় যা কখনও কখনও ব্যয়বহুল অপারেশন হয়। উদাহরণস্বরূপ, যখন meanজেনেরিক S3ফাংশন sumহয় Primitive। সুতরাং উপরে উল্লিখিত কারণগুলির lapply(df, sum)তুলনায় কিছু সময় খুব কার্যকর হতে পারেcolSums

1
খুব সমন্বিত সংক্ষিপ্তসার। মাত্র কয়েকটি নোট: (1) সি কীভাবে "ডেটা.ফ্রেম" গুলি পরিচালনা করতে জানে, যেহেতু তারা গুণাবলী সহ "তালিকা" হয়; এটি এমন যেগুলি colMeansকেবল ম্যাট্রিককে পরিচালনা করতে নির্মিত। (২) আমি আপনার তৃতীয় বিভাগ থেকে কিছুটা বিভ্রান্ত; আমি কী বলতে পারি না - আপনি কী উল্লেখ করছেন? (3) যেহেতু আপনি বিশেষভাবে উল্লেখ করছেন lapply, তাই আমি বিশ্বাস করি যে এটি "[<-"আর এবং সি এর মধ্যে কোনও পার্থক্য করে না ; তারা উভয়ই একটি "তালিকা" (একটি এসএক্সপি) প্রাক-বরাদ্দ করে এবং প্রতিটি পুনরাবৃত্তিতে এটি পূরণ করুন ( SET_VECTOR_ELTসি তে), যদি না আমি আপনার পয়েন্টটি মিস করি।
অ্যালেক্সিস_লাজ

2
আমি আপনার do.callবক্তব্যটি পেয়েছি যে এটি সি পরিবেশে কোনও ফাংশন কলকে বিল্ট করে এবং কেবল এটির মূল্যায়ন করে; যদিও এটিকে লুপিং বা ভেক্টরাইজেশনের সাথে তুলনা করতে আমার খুব কষ্ট হচ্ছে কারণ এটি একটি আলাদা কাজ করে। আপনি প্রকৃতপক্ষে সি এবং আর এর মধ্যে পার্থক্য অ্যাক্সেস এবং নির্ধারণ সম্পর্কে সঠিক, যদিও উভয়ই মাইক্রোসেকেন্ড স্তরে থাকেন এবং ফলাফলকে ব্যাপকভাবে প্রভাবিত করেন না, যেহেতু ব্যয়বহুল পুনরাবৃত্তি আর ফাংশন কল (তুলনা করুন R_loopএবং R_lapplyআমার উত্তরে )। (আমি আপনার পোস্টটি একটি মানদণ্ড দিয়ে সম্পাদনা করব; আশা করি আপনি, তবুও কিছু মনে করবেন না)
অ্যালেক্সিস_লাজ

2
আমি দ্বিমত করার চেষ্টা করছি না --- এবং আমি যে বিষয়ে অসম্মতি জানাচ্ছি সে সম্পর্কে আমি সত্যিই বিভ্রান্ত হয়েছি। আমার আগের মন্তব্যটি আরও ভাল করে বলা যেতে পারে। আমি পরিভাষাটি ব্যবহার হচ্ছে বলে পরিমার্জন করার চেষ্টা করছি কারণ "ভেক্টরাইজেশন" শব্দটির দুটি সংজ্ঞা রয়েছে যা প্রায়শই সঙ্কোচিত হয়। আমি মনে করি না এটি তর্কযোগ্য। বার্নস এবং আপনি এটি কেবল প্রয়োগের অর্থে ব্যবহার করতে চান বলে মনে হয়, তবে হ্যাডলি এবং অনেক আর-কোর সদস্য ( Vectorize()উদাহরণ হিসাবে গ্রহণ করে ) এটি ইউআই অর্থেও ব্যবহার করে। আমি মনে করি যে এই থ্রেডের মধ্যে বেশিরভাগ মতবিরোধ দুটি পৃথক-তবে-সম্পর্কিত ধারণার জন্য একটি শব্দ ব্যবহার করার কারণে হয়েছিল।
গ্রেগর থমাস

3
@ ডেভিড আরেনবার্গ এবং নীচে আর বা সি এর লুপ আছে কিনা তা বিবেচনা না করেই কি এটি কোনও ইউআই অর্থে ভেক্টরাইজেশন নয়?
গ্রেগর টমাস

2
@ ডেভিড আরেনবুর্গ, গ্রেগর, আমি মনে করি এই বিভ্রান্তিটি "কোড ভেক্টরাইজেশন" এবং "ভেক্টরাইজড ফাংশন" এর মধ্যে। আর-তে, ব্যবহারটি পরবর্তীকালের দিকে ঝুঁকছে বলে মনে হচ্ছে। "কোড ভেক্টরাইজেশন" একই নির্দেশে দৈর্ঘ্যের 'কে' দৈর্ঘ্যের একটি ভেক্টরের অপারেটিং বর্ণনা করে। একটি fn মোড়ানো। লুপী কোডের আশেপাশে "ভেক্টরাইজড ফাংশনগুলি" ফলাফল আসে (হ্যাঁ, এটি কোনও অর্থবোধ করে না এবং বিভ্রান্তিকর হয়, আমি একমত, লুপ লুকানো বা ভেক্টর আই / পি ফাংশনগুলি আরও ভাল হবে ) এবং কোড ভেক্টরাইজেশনের সাথে কিছু করার দরকার নেই । আর-তে, প্রয়োগ হ'ল একটি ভেক্টরাইজড ফাংশন হবে তবে এটি আপনার কোডটিকে ভেক্টরাইজ করে না, বরং ভেক্টরগুলিতে পরিচালনা করে।
অরুণ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.