Join GitHub today
GitHub is home to over 50 million developers working together to host and review code, manage projects, and build software together.
Sign up.Call interface for nullmodel simulation #197
Comments
|
@jarioksa : I will have a look and run some tests. |
|
There are a couple of surprising issues with the current implementation. Currently I do not x <- r2dtable(1, rowSums(sipoo), colSums(sipoo))[[1]] # create one matrix
xwork <- x
xnew <- .Call("do_quasiswap", xwork, 1, 1)After this Even when |
|
There is now an alternative Quasiswap function is called in many quantitative null models, but these interfaces still use the old |
|
@jarioksa here are microbenchmark results from Windows 10 and R 3.3.1: Timings are very similar to what you have found on Linux and Mac OS X. Regarding the recommendations: I think it would be best to dichotomize it, i.e. seqential versions might run faster, but there are certain advantages/guarantees to quasiswap if your latest fix makes it truly unbiased. |





I have started implementing
.Call()interface tonullmodelsimulation. Currently we have several compiled C functions and we access these with.C()interface as set up inmake.commsim()and used then bysimulate.nullmodel(). I wanted to see if these functions can be made more efficient by switching from.Cto.Call.In
.Cinterface we pass basic C structures, typically R data as (pointers to) C vectors. The passing of arguments and return of results happens via arguments. In.Callinterface we pass R structures and can return R structures. I have now implemented two test cases which together cover all variants innestedness.c(i.e., sequential and non-sequential models). For sequential methods I have implemented.Callto trial swap, and for non-sequential the.Callto quasiswap. The traditional interface to trial swap is:We first create an empty 3D array
out, then fill the first facet (third dimension,out[,,1]) with swapped input matrix (m), and then we loop over allnsimulations and fill the next matrix with the swapped previous one with repeated.C("trialswap", ...). The new interface is:That's all. We pass an R matrix, create the 3D array within the compiled code, loop over its third facet in compiled code, and finally return the 3D array. The
do_tswapfunction will call the very sametrialswapC code as the old interface, but this call happens within compiled code instead of crossing R/C border. Using.Callinterface normally means moving a part of R into C while keeping the old C.I would appreciate tests and comments on the new interface. The new interface is now available in branch
nestedness-Callsof the devel branch of vegan. The new interfaces can be defined in thenullmodelcommand or inoecosimu. For instance, the version above can be invoked as:The new alternative models are
"dotswap","do2tswap"(an alternative implementation of the same) and"doquasiswap", and the calls to old interfaces also work ("tswap","quasiswap"). The old and new implementation should give equal results with the same random number seed so it should be testing of robustness, flexibility and speed.All other nullmodels can be made as a variant of these or even with the same functions with extra argument of
method, but I want to gain some experience on the code before implementing these for all null models. In particular, I have just started looking at writing code for.Call()interface a couple of weeks ago, and I need to check the code thoroughly. Do you have time to look at these, @psolymos ?My first results are that trial swap indeed is much faster with the
.Call()interface: I have micorbenchmarked 7-fold speed-up for larger data sets (such asBCIandbryceveg), but the effect varies with platform and data properties. However, I have not found any clear benefits with quasiswap. At best I have had <1% speed-up, but the results are inconsistent and sometimes (though more rarely) the new interface is even slower than the old interface. It seems that quasiswap spends most of its time within compiled code anyway, and mere interface is such a small extra cost that it can hardly be measured. I looked at the working of the C code, and it seems that for instance inBCIwe need usually over 1.5 Million cycles to qusiswap the input matrix into 0/1 matrix. Even the tinysipoodata need 150,000 passes to quasiswap the input matrix. The quasiswap algorithm could be easily parallelized, and that could give a clear advantage, and would probably be easier in R than within C. Perhaps it makes sense to keep the old interface for computer-heavy non-sequential methods, but implement sequential methods with.Call.