Rcpp: стоимость S4.slot () - PullRequest
       26

Rcpp: стоимость S4.slot ()

0 голосов
/ 19 марта 2019

В настоящее время я использую классы S4 в пакете с Rcpp и обнаружил потерю производительности по сравнению со старой версией (менее формальной и менее общей) моего кода для выполнения той же задачи.Мне было интересно, было ли узкое место, вызванное многократным вызовом функции S4.slot(), поэтому я создал этот пример в качестве теста:

Сначала определим некоторые классы S4, аналогичные тем, которые я использую: collectionкласс с itemlist и некоторой другой метаинформациейitem - это класс с subitems, который представляет собой числовой вектор и некоторую другую метаинформацию.

setClass("collection", representation(itemlist = "list",
                                      element1 = "numeric",
                                      element2 = "character"))

setClass("item", representation(subitems = "numeric",
                                subitemnames = "character"))

a_item = new("item", subitems = c(1,2),
                      subitemnames = c("A","B"))

a_collection = new("collection", itemlist = replicate(1000000, a_item),
                                 element1 = 123, element2 = "something")

Я пытаюсь работать с объектом collection, используя все его itemlist,Поэтому я создал функцию Rcpp для sum всех элементов каждого itemlist и добавил их вместе.

cfn_s4 <-
  'double operation(S4 collection, NumericVector z){
    double res = 0;
    List all_items = collection.slot("itemlist");
    for(int i = 0; i < z.size(); i++){
      S4 this_item = all_items[i];
      NumericVector to_add = this_item.slot("subitems");
      res = res + sum(to_add)*z[i];
    }
    return(res);
  }'

Я также создал другую версию, которая делает то же самое, но принимает в качестве аргумента list(S4 здесь не используется).Требуется некоторая предварительная обработка, я теряю некоторую метаинформацию, но нет необходимости звонить сюда S4.slot.

cfn_list <-   
'double operation_list(List collection, NumericVector z){
    double res = 0;
    for(int i = 0; i < z.size(); i++){
      NumericVector to_add = collection[i];
      res = res + sum(to_add)*z[i];
    }
    return(res);
  }'

Вот критерии:

library(Rcpp)
library(rbenchmark)
fn_s4 <- cppFunction(cfn_s4)
fn_list <- cppFunction(cfn_list)

set.seed(1)
z <- rnorm(1000)

ready_list <- map(a_collection@itemlist, "subitems")

benchmark(fn_s4(a_collection, z),
          fn_list(ready_list, z))
                    test replications elapsed relative user.self sys.self user.child sys.child
2 fn_list(ready_list, z)          100    0.00       NA      0.00        0         NA        NA
1 fn_s4(a_collection, z)          100    0.01       NA      0.02        0         NA        NA

Даже дляобъект collection со списком из миллиона элементов заставил меня подумать, что даже при миллионе вызовов S4.slot в функции не было никакого снижения производительности, поэтому стоимость S4.slot незначительна.Это действительно так?Я подумал, что использование классов S4, особенно когда Rcpp не знает, кто они на самом деле, должно замедлить работу кода из-за некоторых возможных требуемых преобразований типов.

Могу ли я доверять этому тесту и предположить, что значительная потеря производительности не вызванапозвонив S4.slot хотя бы миллионы раз?Можете ли вы увидеть что-то не так с моими функциями, приводящими к несправедливым сравнениям?

...