
On 21 Sep 2012, at 17:48, Robert Castelo wrote:

> On 09/21/2012 06:32 PM, Vincent Carey wrote:
>> On Fri, Sep 21, 2012 at 12:26 PM, Robert Castelo<robert.castelo@upf.edu>  wrote:
>>> hi Martin,
>>> 
>>> 
>>> On 09/21/2012 05:59 PM, Martin Morgan wrote:
>>>> 
>>>> On 09/21/2012 05:05 AM, Benilton Carvalho wrote:
>>>>> 
>>>>> Replace
>>>>> 
>>>>> Biobase::`annotation<-`(eScoEset, "")
>>>>> 
>>>>> by
>>>>> 
>>>>> Biobase::`annotation<-`(eScoEset, value="")
>>>> 
>>>> 
>>>> why not just
>>>> 
>>>> annotation(eScoEset)<- ""
>>>> 
>>>> with importFrom(Biobase, "annotation<-") in the NAMESPACE?
>>> 
>>> 
>>> well, my understanding of the general policy to use classes and methods
>>> defined in other packages is that i should *always* "import" them in the
>>> NAMESPACE file.
>>> 
>>> however, somehow i feel more comfortable using this pkg::f() idiom when i
>>> call something defined somewhere else because just reading the code i know
>>> immediately where imported things come from.
>>> 
>>> is there any reason related to performance, correctness or coding style by
>>> which you think i should not do it?
>> 
>>> unix.time( for (i in 1:100000 ) base::sin )
>>    user  system elapsed
>>   1.109   0.004   1.120
>>> unix.time( for (i in 1:100000 ) sin )
>>    user  system elapsed
>>   0.010   0.000   0.011
>> 
>> ymmv
> 
> =8-o
> 
> i'll take it into account.


I would say that the pkg::f() idiom is still to be preferred, especially for S4 methods. The example above doesn't tell all the story. The timing is for evaluating the symbol only. In the case of S4 methods, that timing will be for obtaining the standard generic function and does not account for the dispatch time!

One typical example where I care for performance is a call to rep.int() from the base package. On my system, without any extra libraries loaded I get:

> rep.int

function (x, times) 
.Internal(rep.int(x, times))
<bytecode: 0x100cab0b0>
<environment: namespace:base>


> replicate(5, system.time(for(i in seq_len(1e5L)) rep.int(1L, times = 1e4L)))
            [,1]  [,2]  [,3]  [,4]  [,5]
user.self  2.348 2.378 2.416 2.359 2.309
sys.self   0.026 0.016 0.015 0.013 0.010
elapsed    2.353 2.391 2.455 2.359 2.301
user.child 0.000 0.000 0.000 0.000 0.000
sys.child  0.000 0.000 0.000 0.000 0.000


There is a penalty if you use the '::'operator, as Vincent and Martin mentioned:

> replicate(5, system.time(for(i in seq_len(1e5L)) base::rep.int(1L, times = 1e4L)))
            [,1]  [,2]  [,3]  [,4]  [,5]
user.self  4.138 4.116 4.102 4.124 4.130
sys.self   0.020 0.022 0.018 0.022 0.021
elapsed    4.125 4.105 4.081 4.110 4.119
user.child 0.000 0.000 0.000 0.000 0.000
sys.child  0.000 0.000 0.000 0.000 0.000


However, after loading the IRanges library which exports rep.int() as a S4 method, you start dealing with the S4 dispatch mechanism and in this case using the '::' operator can improve performance:


> library(IRanges)
> rep.int
standardGeneric for "rep.int" defined from package "BiocGenerics"

function (x, ...) 
standardGeneric("rep.int")
<environment: 0x11c1bf008>
Methods may be defined for arguments: x
Use  showMethods("rep.int")  for currently available ones.

> replicate(5, system.time(for(i in seq_len(1e5L)) rep.int(1L, times = 1e4L)))
            [,1]  [,2]  [,3]  [,4]  [,5]
user.self  8.767 8.737 8.634 8.754 8.822
sys.self   0.062 0.045 0.043 0.046 0.061
elapsed    8.757 8.714 8.604 8.745 9.030
user.child 0.000 0.000 0.000 0.000 0.000
sys.child  0.000 0.000 0.000 0.000 0.000

> replicate(5, system.time(for(i in seq_len(1e5L)) base::rep.int(1L, times = 1e4L)))
        [,1]  [,2]  [,3]  [,4]  [,5]
user.self  4.896 4.990 4.902 4.992 4.867
sys.self   0.026 0.031 0.029 0.023 0.027
elapsed    4.901 5.028 4.933 4.989 4.875
user.child 0.000 0.000 0.000 0.000 0.000
sys.child  0.000 0.000 0.000 0.000 0.000


What we are doing in this case is to overpass the S4 dispatch mechanism, and this is where the gain is coming from. If you want to also remove the '::' penalty (lets say you have a big loop) you can define a new function having the same body as base::rep.int():

> baseRepInt <- base::rep.int
> replicate(5, system.time(for(i in seq_len(1e5L)) baseRepInt(1L, times = 1e4L)))
            [,1]  [,2]  [,3]  [,4]  [,5]
user.self  3.398 3.287 3.383 3.375 3.257
sys.self   0.020 0.020 0.017 0.020 0.019
elapsed    3.392 3.291 3.367 3.371 3.242
user.child 0.000 0.000 0.000 0.000 0.000
sys.child  0.000 0.000 0.000 0.000 0.000


So as usual there is a tradeoff between having methods and functions, allowing for type checking, etc. It is also a bit surprising that there is a penalty even for .Internal() after IRanges is loaded ~ 2.40sec versus ~ 3.30sec after! 
I thus prefer the pkg::f() idiom especially when there are many generics for a basic function. 

Hope this helps.


Adrian 





> 
> thanks!
> robert.
> 
> _______________________________________________
> Bioc-devel@r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/bioc-devel


	[[alternative HTML version deleted]]

