jit {jit} | R Documentation |
Enable just-in-time (JIT) compilation of a block of R code. Loops in a JIT block are generally much faster.
This function is only useful under Ra. It has no effect under standard R. See http://www.milbo.users.sonic.net/ra/index.html.
This help page is for Ra 1.1.1 and higher.
jit(jit = NA, trace = 0)
jit |
NA make no changes but return the current JIT state (default)
0 end JIT block — using jit(0) is often unnecessary because an implicit jit(0) is
performed when the function returns
1 start JIT block 2 start JIT block with extra optimization of nested loops (fast but potentially dangerous, see below) |
trace |
0 silent (default)
1 show which expressions are compiled to JIT code 2 show compilation details 3 show code generation details |
Nomenclature
A JIT block is the R code between jit(1)
and
jit(0)
or the end of the function.
Jitted code is R code that has been compiled by the just-in-time compiler (to a form where it will execute more quickly). Not all code in a JIT block is necessarily jitted.
A jitted variable is a variable used in jitted code.
What gets jitted?
The following operations are jitted, but only if they are in a for/while/repeat loop in a JIT block:
x <- y
x[i] <- y[j]
(but not double subscripts: x[i,j]
)
Functions called from the JIT block are not jitted.
For example, the for
in the first line of code below is not
compiled because it is called from within system.time
.
jit(1); system.time({ for(i in 1:100) x<-x+i }) # is not jitted system.time({ jit(1); for(i in 1:100) x<-x+i }) # is jittedUse
trace>=1
to see what was jitted
(and possibly modify your code to enable more jitting).
Code in a JIT block that is not actually jitted will run as
normal R code and coexist happily with jitted code.
Summary of the differences between standard R and jitted code
The semantics of jitted code differ from standard R where retaining compatibility would have made the jitted code substantially slower. Bear in mind that not everything in a JIT block is necessarily jitted. Non-jitted code in a JIT block retains R's usual semantics.
The short version of the next few sections is:
The JIT compiler will not allow you to change the length or type of a jitted variable. (For this reason, the "L" suffix to define an integer constant 123L is useful in JIT blocks, although seldom used in standard R code.) Here "type" means the type returned by typeof.
The JIT compiler will not allow you to use functions like attach in a JIT block or in functions called from the JIT block. The idea is to keep the environment stable so jitted code can be efficient.
NAs of type integer and logical are not handled in jitted code — the next section gives details.
The results of comparison to NaN differ between standard R and jitted code — the next section gives details.
Integer arithmetic overflows are detected in standard R but not in jitted code
Most attributes of jitted variables are dropped.
An out-of-range index in a jitted expression will cause an error message
(the options check.bounds
setting is ignored):
x <- 1; x[3] # NA in standard R but "Error: out-of-range index" when jittedThe value of a loop is the last evaluated loop body in standard R, but NULL in jitted code:
x <- for (i in 1:3) 99 # x is set to 99 in standard R but NULL when jitting
NaNs and NAs
NaNs: In jitted code NaNs (always double) are handled directly by the machine hardware, as is usually the case in standard R. (We assume IEEE 754 hardware http://en.wikipedia.org/wiki/IEEE_754.) The hardware knows about double NaNs: where necessary it will generate them (zero divided by zero is NaN) and propagate them (NaN + 123 is NaN).
Double NAs: In R, one of the many possible values of NaN provided by the hardware is defined to be the double NA. The hardware therefore makes no distinction between double NAs and other NaNs. Standard R evaluation has some software to handle double NAs over and above the hardware (mostly in type conversions, which don't get jitted anyway); jitted code has no such software. Thus in jitted code, double NAs behave identically to NaNs.
Integer and logical NAs:
Integer and logical NAs
are meaningless in jitted code, and will cause incorrect results if used.
Integer and logical NAs are represented by INT_MIN
internally in R.
Here INT_MIN
is the minimum integer representable in a machine word.
(On a 32 bit machine, INT_MIN
is about -2e9.)
The hardware does not know about integer and logical NAs.
In standard R these are therefore handled in software;
in jitted code they are treated like any other integer.
Thus a logical or integer
NA in jitted code is treated as
an integer with the value INT_MIN
(with a logical value of TRUE).
Remember that a plain NA in your program text is treated
as logical by R and thus as INT_MIN
in jitted code.
In the standard R code below, the logical NA is
converted to a double NA (because 1.2 is double, making the
whole expression double):
1.2 + NA # evaluates to NA in standard R
In jitted code, the NA is treated by the machine
hardware as an integer with the value INT_MIN
and is
thus converted to a double with the value INT_MIN
:
1.2 + NA # evaluates to 1.2 + INT_MIN in jitted code
.
Summarizing, avoid NAs in jitted code unless you are sure that your NA is a double.
In jitted code, comparing anything to a NaN results in FALSE
,
as per IEEE 754:
standard R jitted 1.2 == NaN NA FALSE 1.2 > NaN NA FALSE NaN == NaN NA FALSE (sic)
Other discrepancies (these were tested on a Windows XP Pentium D system):
standard R jitted 0 ^ NaN Inf (why?) NaN 1L %/% 0L integer NA runtime error message 1L %% 0L integer NA runtime error messageThe last two are justified by the fact that integer NAs are meaningless in jitted code so it does not make sense to generate one. On the other hand, standard division "/" of an integer by zero evaluates to double
Inf
in both standard R and jitted code.
Optimizing nested loops with jit(2)
Use jit(2)
instead of jit(1)
for extra optimization
of inner nested loops.
In the example below the inner j
loop will
undergo this optimization:
jit(2) for (i in 1:n) { # same as jit(1) because is outermost loop ... for (j in 1:m) # extra optimization with jit(2), 1:m must not change ... # any further loops in here also optimized with jit(2) ... }To be optimized this way the entire inner loop body must be jittable and the loop sequence (
1:m
above) must have integer type.
There are some important conditions when using jit(2)
.
The danger is that R will usually not warn you
if your code doesn't meet a condition — you will just get wrong results.
Because of this danger, jit(2)
is considered experimental at present.
The conditions are:
1:m
in the example above) is calculated just once and
thereafter assumed constant for the entire JIT block.
Thus the example code above will give incorrect results without warning
if m
changes between the curly braces (which would require
a recalculation of 1:m
).
It is fine to change n
since this affects only the outer loop.
break
or next
in the inner loop (they
will incorrectly break to the outer loop, but please don't rely on this).
It is, however, ok to use return
in the inner loop.
jit(2) for (i in 1:3) for (j in 1:3) j = 9L # error msg: assignment to loop variable "j"
Limits of the current implementation
You can JIT only one function at any time.
A call to jit
in a function that is called from a JIT block
is ignored with a warning message.
Jitted code uses more memory than standard R. This is because jitted code does not release the temporary buffers used to evaluate expressions until the end of the JIT block. Standard R allocates and releases temporary buffers as it evaluates each expression.
More types of expression will be jitted in future releases.
Note for package writers
If your package benefits from jit
, then
we suggest that you simply make your package require jit
(by putting Depends:jit
in the DESCRIPTION
file).
People can then use standard R with your package —
calls to jit()
will have no effect but will not cause warnings or errors —
but people who use Ra will get speedups.
You could put a remark in your package documentation saying
that speed will be improved using Ra instead of R.
A three element integer vector.
Under R
All elements are 0.
Under Ra
[1]
The value of the last specified jit
argument to jit()
,
so is 1
or 2
if in a JIT block, else 0
.
Is 0
in functions called from the JIT block.
[2]
The value of the last specified trace
argument to jit()
.
Is 0
in functions called from the JIT block.
[3]
The value of the last specified jit
argument to jit()
.
Retains its value in functions called from the JIT block.
Be careful when using jit()
as an argument to a function. It will be evaluated
as if called from that function. Example:
jit(1) jit()[1] # is 1 print(jit()[1]) # prints 0, because jit() is called from print jit.flag <- jit()[1] print(jit.flag) # prints 1
For more information on the jitter see http://www.milbo.users.sonic.net/ra/index.html
nojit
is.ra
jitter
is not related
foo <- function(N) { jit(jit.flag) x <- 0 for (i in 1:N) x <- x + 1 x } N = 3e5 jit.flag <- 0; time.nojit <- system.time(foo(N))[1] jit.flag <- 1; time.jit <- system.time(foo(N))[1] cat("Time ratio", time.jit / time.nojit, "\n") jit(0)