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

Reply via email to