[Rd] grDevices::convertColor and colorRamp(space='Lab') Performance Improvements

Brodie Gaslam brodie@g@@l@m @ending from y@hoo@com
Wed Oct 3 03:13:44 CEST 2018

`grDevices::convertColor` performance can be improved by 30-300x with 
small changes to the code.  `colorRamp(space='Lab')` uses `convertColor` 
so it too benefits from substantial performance gains.

`convertColor` vectorizes explicitly over the rows of the input color 
matrix using `apply`. The level-1 patch [1] illustrates a possible 
minimal set of changes to achieve this with just R matrix and vector 
operations.  The changes consist primarily of switching `if/else` to 
`ifelse`, `c` to `cbind`, `sum` to `rowSums`, etc. This results in 
speedups of 30-100x as shown in table 1:

from        Apple RGB  sRGB CIE RGB   XYZ  Lab  Luv
   Apple RGB        NA  38.3    55.8  30.3 60.2 56.3
   sRGB           38.7    NA    55.7  36.5 62.9 52.7
   CIE RGB        45.2  44.4      NA  30.6 51.5 43.1
   XYZ            73.4  57.5    69.1    NA 92.2 69.0
   Lab            46.6  56.6    65.4  72.0   NA 61.3
   Luv            73.2 107.3    67.3 105.8 97.8   NA

## Table 1:
## Ratios of `grDevices` to 'level-1' patch speeds for 8000 colors
## from each supported color space to all other supported color spaces.

A few incremental changes as detailed in the level-2 patch [2] yield 
additional performance improvements:

from        Apple RGB  sRGB CIE RGB   XYZ Lab   Luv
   Apple RGB        NA  97.1   106.2  89.0 117  83.4
   sRGB           92.5    NA    99.4  86.4 120  76.0
   CIE RGB       119.2 184.2      NA  82.2 135  83.4
   XYZ           122.3 209.8   140.9    NA 171 148.8
   Lab           166.4 168.2   255.4 288.5  NA 265.1
   Luv           141.7 173.6   279.6 310.1 195    NA

## Table 2:
## Ratios of `grDevices` to level-2 patch speeds for 8000 colors
## from each supported color space to all other supported color spaces.

Not shown here is that the patched versions of `convertColor` are faster 
with small inputs as well, though the difference is less marked.

I have posted tests on github [3], along with the results of running 
them on my system [4].  While these tests are not proof that the patches 
do not change the function other than to make it faster, the tests 
results combined with the narrow scope of the changes hopefully provides 
sufficient evidence this is the case.   For those wanting to run the 
tests, installation and testing instructions are on the github landing 
page for this project [5].

There are some minor (in my opinion) changes in behavior that need to be 

* Inputs that would previously stop with errors or work inconsistently 
now work consistently (e.g. zero-row inputs or inputs containing 
* Column names are consistently set to the color space initials; these 
were previously inconsistently set / mangled by `c`.

These are discussed at length with examples in [6].

It would be possible to preserve the existing behavior, but doing so 
would require some contortions that serve no other purposes than that. 
Additionally, in many cases the existing behavior is inconsistent across 
different input/output color spaces.  Finally, most of the differences 
involve corner case inputs such as those containing NA/NaN/Inf, or zero 

I am entirely amenable to modify the patches to preserve existing 
behavior in these cases if that is considered desirable.

These patches should be coordinated with #17473 [7], which I found while 
working on this issue.


'level-1' patch:

'level-2' patch:

Tests on github, and the result of running them:
[3]: https://github.com/brodieG/grDevices2/blob/level-2/tests/convertColor.R

Github landing page for this project:
[5]: https://github.com/brodieG/grDevices2

Discussion of differences introduces by patches:

Indirectly related bugzilla issue #17473:
[7]: https://bugs.r-project.org/bugzilla/show_bug.cgi?id=17473

More information about the R-devel mailing list