Hi folks, I've attached a patch to the svn trunk that improves the performance of the serialize/unserialize interface for vector types. The current implementation: a) invokes the R_XDREncode operation for each element of the vector type, and b) uses a switch statement to determine the stream type for each element of the vector type. I've added R_XDREncodeVector/R_XDRDecodeVector functions that accept N elements at a time, and I've reorganized the implementation so that the stream type is not queried once per element.
In the following microbenchmark (below), I've observed performance improvements of about x2.4. In a real benchmark that is using the serialization interface to make MPI calls, I see about a 10% improvement in performance. Cheers, --Michael microbenchmark: input <- matrix(1:100000000, 10000, 10000) output <- serialize(input, NULL) for(i in 1:10) { print(system.time(serialize(input, NULL))) } for(i in 1:10) { print(system.time(unserialize(output))) }
Index: src/include/Rinternals.h =================================================================== --- src/include/Rinternals.h (revision 57107) +++ src/include/Rinternals.h (working copy) @@ -749,6 +749,7 @@ void Rf_warningcall_immediate(SEXP, const char *, ...); /* Save/Load Interface */ +#define R_XDR_COMPLEX_SIZE 16 #define R_XDR_DOUBLE_SIZE 8 #define R_XDR_INTEGER_SIZE 4 @@ -757,6 +758,13 @@ void R_XDREncodeInteger(int i, void *buf); int R_XDRDecodeInteger(void *buf); +void R_XDREncodeDoubleVector(double *d, void *buf, int len); +void R_XDRDecodeDoubleVector(void *input, double *output, int len); +void R_XDREncodeComplexVector(Rcomplex *c, void *buf, int len); +void R_XDRDecodeComplexVector(void *input, Rcomplex *output, int len); +void R_XDREncodeIntegerVector(int *i, void *buf, int len); +void R_XDRDecodeIntegerVector(void *input, int *output, int len); + typedef void *R_pstream_data_t; typedef enum { Index: src/main/serialize.c =================================================================== --- src/main/serialize.c (revision 57107) +++ src/main/serialize.c (working copy) @@ -792,20 +792,62 @@ WriteItem(STRING_ELT(s, i), ref_table, stream); } -/* e.g., OutVec(fp, obj, INTEGER, OutInteger) */ -#define OutVec(fp, obj, accessor, outfunc) \ - do { \ - int cnt; \ - for (cnt = 0; cnt < LENGTH(obj); ++cnt) \ - outfunc(fp, accessor(obj, cnt)); \ - } while (0) - -#define LOGICAL_ELT(x,__i__) LOGICAL(x)[__i__] #define INTEGER_ELT(x,__i__) INTEGER(x)[__i__] #define REAL_ELT(x,__i__) REAL(x)[__i__] #define COMPLEX_ELT(x,__i__) COMPLEX(x)[__i__] #define RAW_ELT(x,__i__) RAW(x)[__i__] +#define OutVec(NAME, CAPNAME, XDR, CAPXDR, TYPE) \ +static R_INLINE void Out ## NAME ## Vec(R_outpstream_t stream, SEXP s, int length) \ +{ \ + OutInteger(stream, length); \ + switch (stream->type) { \ + case R_pstream_xdr_format: \ + if (length > (128 / R_XDR_## CAPXDR ##_SIZE)) \ + { \ + char *buf = Calloc( R_XDR_ ## CAPXDR ## _SIZE * length, char); \ + R_XDREncode ## XDR ## Vector(CAPNAME(s), buf, length); \ + stream->OutBytes(stream, buf, R_XDR_ ## CAPXDR ## _SIZE * length); \ + Free(buf); \ + } else { \ + char buf[128]; \ + R_XDREncode ## XDR ## Vector(CAPNAME(s), buf, length); \ + stream->OutBytes(stream, buf, R_XDR_ ## CAPXDR ## _SIZE * length); \ + } \ + break; \ + case R_pstream_binary_format: \ + stream->OutBytes(stream, CAPNAME(s), sizeof(TYPE) * length); \ + break; \ + default: \ + { \ + int cnt; \ + for (cnt = 0; cnt < length; ++cnt) \ + Out ## NAME(stream, CAPNAME ## _ELT(s, cnt)); \ + } \ + } \ +} + +OutVec(Integer, INTEGER, Integer, INTEGER, int) +OutVec(Real, REAL, Double, DOUBLE, double) +OutVec(Complex, COMPLEX, Complex, COMPLEX, Rcomplex) + +static R_INLINE void OutByteVec(R_outpstream_t stream, SEXP s, int length) +{ + OutInteger(stream, length); + switch (stream->type) { + case R_pstream_xdr_format: + case R_pstream_binary_format: + stream->OutBytes(stream, RAW(s), length); + break; + default: + { + int cnt; + for (cnt = 0; cnt < length; ++cnt) + OutByte(stream, RAW_ELT(s, cnt)); + } + } +} + static void WriteItem (SEXP s, SEXP ref_table, R_outpstream_t stream) { int i; @@ -932,16 +974,13 @@ break; case LGLSXP: case INTSXP: - OutInteger(stream, LENGTH(s)); - OutVec(stream, s, INTEGER_ELT, OutInteger); + OutIntegerVec(stream, s, LENGTH(s)); break; case REALSXP: - OutInteger(stream, LENGTH(s)); - OutVec(stream, s, REAL_ELT, OutReal); + OutRealVec(stream, s, LENGTH(s)); break; case CPLXSXP: - OutInteger(stream, LENGTH(s)); - OutVec(stream, s, COMPLEX_ELT, OutComplex); + OutComplexVec(stream, s, LENGTH(s)); break; case STRSXP: OutInteger(stream, LENGTH(s)); @@ -962,8 +1001,7 @@ error(_("this version of R cannot write byte code objects")); #endif case RAWSXP: - OutInteger(stream, LENGTH(s)); - OutVec(stream, s, RAW_ELT, OutByte); + OutByteVec(stream, s, LENGTH(s)); break; case S4SXP: break; /* only attributes (i.e., slots) count */ @@ -1214,21 +1252,44 @@ return s; } -#define InVec(fp, obj, accessor, infunc, length) \ - do { \ - int cnt; \ - for (cnt = 0; cnt < length; ++cnt) \ - accessor(obj, cnt, infunc(fp)); \ - } while (0) +#define InVec(NAME, CAPNAME, XDR, CAPXDR, TYPE) \ +static R_INLINE void In ## NAME ## Vec(R_inpstream_t stream, SEXP obj, int length) \ +{ \ + switch (stream->type) { \ + case R_pstream_xdr_format: \ + if (length > (128 / R_XDR_## CAPXDR ##_SIZE)) \ + { \ + char *buf = Calloc( R_XDR_ ## CAPXDR ## _SIZE * length, char); \ + stream->InBytes(stream, buf, R_XDR_ ## CAPXDR ## _SIZE * length); \ + R_XDRDecode ## XDR ## Vector(buf, CAPNAME(obj), length); \ + Free(buf); \ + } else { \ + char buf[128]; \ + stream->InBytes(stream, buf, R_XDR_ ## CAPXDR ## _SIZE * length); \ + R_XDRDecode ## XDR ## Vector(buf, CAPNAME(obj), length); \ + } \ + break; \ + case R_pstream_binary_format: \ + stream->InBytes(stream, CAPNAME(obj), sizeof(TYPE) * length); \ + break; \ + default: \ + { \ + int cnt; \ + for (cnt = 0; cnt < length; ++cnt) \ + SET_ ## CAPNAME ## _ELT(obj, cnt, In ## NAME(stream)); \ + } \ + } \ +} - - -#define SET_LOGICAL_ELT(x,__i__,v) (LOGICAL_ELT(x,__i__)=(v)) #define SET_INTEGER_ELT(x,__i__,v) (INTEGER_ELT(x,__i__)=(v)) #define SET_REAL_ELT(x,__i__,v) (REAL_ELT(x,__i__)=(v)) #define SET_COMPLEX_ELT(x,__i__,v) (COMPLEX_ELT(x,__i__)=(v)) #define SET_RAW_ELT(x,__i__,v) (RAW_ELT(x,__i__)=(v)) +InVec(Integer, INTEGER, Integer, INTEGER, int) +InVec(Real, REAL, Double, DOUBLE, double) +InVec(Complex, COMPLEX, Complex, COMPLEX, Rcomplex) + static SEXP ReadItem (SEXP ref_table, R_inpstream_t stream) { SEXPTYPE type; @@ -1379,24 +1440,20 @@ } break; case LGLSXP: - length = InInteger(stream); - PROTECT(s = allocVector(type, length)); - InVec(stream, s, SET_LOGICAL_ELT, InInteger, length); - break; case INTSXP: length = InInteger(stream); PROTECT(s = allocVector(type, length)); - InVec(stream, s, SET_INTEGER_ELT, InInteger, length); + InIntegerVec(stream, s, length); break; case REALSXP: length = InInteger(stream); PROTECT(s = allocVector(type, length)); - InVec(stream, s, SET_REAL_ELT, InReal, length); + InRealVec(stream, s, length); break; case CPLXSXP: length = InInteger(stream); PROTECT(s = allocVector(type, length)); - InVec(stream, s, SET_COMPLEX_ELT, InComplex, length); + InComplexVec(stream, s, length); break; case STRSXP: length = InInteger(stream); Index: src/main/saveload.c =================================================================== --- src/main/saveload.c (revision 57107) +++ src/main/saveload.c (working copy) @@ -2079,6 +2079,7 @@ } /* defined in Rinternals.h +#define R_XDR_COMPLEX_SIZE 16 #define R_XDR_DOUBLE_SIZE 8 #define R_XDR_INTEGER_SIZE 4 */ @@ -2134,6 +2135,101 @@ return i; } +void attribute_hidden R_XDREncodeDoubleVector(double *d, void *buf, int len) +{ + XDR xdrs; + int cnt, success = 1; + + xdrmem_create(&xdrs, (char *) buf, len * R_XDR_DOUBLE_SIZE, XDR_ENCODE); + for(cnt = 0; cnt < len && success; cnt++) { + success = xdr_double(&xdrs, d + cnt); + } + + xdr_destroy(&xdrs); + if (! success) + error(_("XDR write failed")); +} + +void attribute_hidden R_XDRDecodeDoubleVector(void *input, double *output, int len) +{ + XDR xdrs; + int cnt, success = 1; + + xdrmem_create(&xdrs, (char*) input, len * R_XDR_DOUBLE_SIZE, XDR_DECODE); + + for(cnt = 0; cnt < len && success; cnt++) { + success = xdr_double(&xdrs, output + cnt); + } + + xdr_destroy(&xdrs); + if (! success) + error(_("XDR read failed")); +} + +void attribute_hidden R_XDREncodeComplexVector(Rcomplex *c, void *buf, int len) +{ + XDR xdrs; + int cnt, success = 1; + + xdrmem_create(&xdrs, (char *) buf, len * R_XDR_COMPLEX_SIZE, XDR_ENCODE); + for(cnt = 0; cnt < len && success; cnt++) { + success = xdr_double(&xdrs, &(c[cnt].r)); + if (success) success = xdr_double(&xdrs, &(c[cnt].i)); + } + + xdr_destroy(&xdrs); + if (! success) + error(_("XDR write failed")); +} + +void attribute_hidden R_XDRDecodeComplexVector(void *input, Rcomplex *output, int len) +{ + XDR xdrs; + int cnt, success = 1; + + xdrmem_create(&xdrs, (char*) input, len * R_XDR_COMPLEX_SIZE, XDR_DECODE); + + for(cnt = 0; cnt < len && success; cnt++) { + success = xdr_double(&xdrs, &(output[cnt].r)); + if (success) success = xdr_double(&xdrs, &(output[cnt].i)); + } + + xdr_destroy(&xdrs); + if (! success) + error(_("XDR read failed")); +} + +void attribute_hidden R_XDREncodeIntegerVector(int *i, void *buf, int len) +{ + XDR xdrs; + int cnt, success = 1; + + xdrmem_create(&xdrs, (char *) buf, len * R_XDR_INTEGER_SIZE, XDR_ENCODE); + for(cnt = 0; cnt < len && success; cnt++) { + success = xdr_int(&xdrs, i + cnt); + } + + xdr_destroy(&xdrs); + if (! success) + error(_("XDR write failed")); +} + +void attribute_hidden R_XDRDecodeIntegerVector(void *input, int *output, int len) +{ + XDR xdrs; + int cnt, success = 1; + + xdrmem_create(&xdrs, (char*) input, len * R_XDR_INTEGER_SIZE, XDR_DECODE); + + for(cnt = 0; cnt < len && success; cnt++) { + success = xdr_int(&xdrs, output + cnt); + } + + xdr_destroy(&xdrs); + if (! success) + error(_("XDR read failed")); +} + /* Next two were used in gnomeGUI package, are in Rinterface.h */ void R_SaveGlobalEnvToFile(const char *name) {
______________________________________________ R-devel@r-project.org mailing list https://stat.ethz.ch/mailman/listinfo/r-devel