Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
  • 2 commits
  • 1 file changed
  • 2 comments
  • 1 contributor
Nov 25, 2011
Nico Kruber JInterface: improved OtpOutputStream buffer allocation
Previously, the buffer was increased linearly by 2048 bytes.
I now propose to use an exponential increase function (similar to Javas ArrayList, e.g. always at least +50%).
This significantly increases performance of e.g. doRPC for large parameters as the following comparison illustrates (shown is the buffer size after each time, the buffer has reached its limit):

n    n*2048       (n*3)/2+1    (n*3)/2+1 (at least +2048)
1     2,048           2,048          2,048
2     4,096           3,073          4,096
3     6,144           4,610          6,145
4     8,192           6,916          9,218
5     10,240         10,375         13,828
6     12,288         15,563         20,743
7     14,336         23,345         31,115
8     16,384         35,018         46,673
9     18,432         52,528         70,010
10    20,480         78,793        105,016
11    22,528        118,190        157,525
12    24,576        177,286        236,288
13    26,624        265,930        354,433
14    28,672        398,896        531,650
15    30,720        598,345        797,476
16    32,768        897,518      1,196,215
17    34,816      1,346,278      1,794,323
18    36,864      2,019,418      2,691,485
19    38,912      3,029,128      4,037,228
20    40,960      4,543,693      6,055,843
21    43,008      6,815,540      9,083,765
22    45,056     10,223,311     13,625,648
23    47,104     15,334,967     20,438,473
24    49,152     23,002,451     30,657,710
25    51,200     34,503,677     45,986,566
26    53,248     51,755,516     68,979,850
27    55,296     77,633,275    103,469,776
28    57,344    116,449,913    155,204,665
29    59,392    174,674,870    232,806,998
30    61,440    262,012,306    349,210,498

Actually, ArrayList uses the (n*3)/2+1 strategy. In order not to decrease performance for messages <10k, we could keep the (public) OtpOutputStream#defaultIncrement constant and let the buffer always increase by at least this much (third column).

In order to create a buffer of 1MB, now only 16 array copies are needed vs. (1024*1024/2048)=512 array copies for the linear increase function. If a user sends a message of 10MB size, this is 22 vs. 5120 copies.

NOTE: the meaning of the "public static final int defaultIncrement" member has changed a bit with this implementation (API compatibility?) - why was this public in the first place?
22f2f43
Nov 30, 2011
Nico Kruber - restored Java5 compatibility a195e9b
57  lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java
@@ -25,6 +25,7 @@
25 25
 import java.math.BigDecimal;
26 26
 import java.math.BigInteger;
27 27
 import java.text.DecimalFormat;
  28
+import java.util.Arrays;
28 29
 
29 30
 /**
30 31
  * Provides a stream for encoding Erlang terms to external format, for
@@ -38,8 +39,8 @@
38 39
 public class OtpOutputStream extends ByteArrayOutputStream {
39 40
     /** The default initial size of the stream. * */
40 41
     public static final int defaultInitialSize = 2048;
41  
-
42  
-    /** The default increment used when growing the stream. * */
  42
+ 
  43
+    /** The default increment used when growing the stream (increment at least this much). * */
43 44
     public static final int defaultIncrement = 2048;
44 45
 
45 46
     // static formats, used to encode floats and doubles
@@ -95,6 +96,41 @@ public int getPos() {
95 96
     }
96 97
 
97 98
     /**
  99
+     * Trims the capacity of this <tt>OtpOutputStream</tt> instance to be the
  100
+     * buffer's current size.  An application can use this operation to minimize
  101
+     * the storage of an <tt>OtpOutputStream</tt> instance.
  102
+     */
  103
+    public void trimToSize() {
  104
+	if (super.count < super.buf.length) {
  105
+	    final byte[] tmp = new byte[super.count];
  106
+	    System.arraycopy(super.buf, 0, tmp, 0, super.count);
  107
+	    super.buf = tmp;
  108
+	}
  109
+    }
  110
+
  111
+    /**
  112
+     * Increases the capacity of this <tt>OtpOutputStream</tt> instance, if
  113
+     * necessary, to ensure that it can hold at least the number of elements
  114
+     * specified by the minimum capacity argument.
  115
+     *
  116
+     * @param   minCapacity   the desired minimum capacity
  117
+     */
  118
+    public void ensureCapacity(int minCapacity) {
  119
+	int oldCapacity = super.buf.length;
  120
+	if (minCapacity > oldCapacity) {
  121
+	    int newCapacity = (oldCapacity * 3)/2 + 1;
  122
+	    if (newCapacity < oldCapacity + defaultIncrement)
  123
+		newCapacity = oldCapacity + defaultIncrement;
  124
+	    if (newCapacity < minCapacity)
  125
+		newCapacity = minCapacity;
  126
+	    // minCapacity is usually close to size, so this is a win:
  127
+	    final byte[] tmp = new byte[newCapacity];
  128
+	    System.arraycopy(super.buf, 0, tmp, 0, super.count);
  129
+	    super.buf = tmp;
  130
+	}
  131
+    }
  132
+
  133
+    /**
98 134
      * Write one byte to the stream.
99 135
      * 
100 136
      * @param b
@@ -102,13 +138,7 @@ public int getPos() {
102 138
      * 
103 139
      */
104 140
     public void write(final byte b) {
105  
-	if (super.count >= super.buf.length) {
106  
-	    // System.err.println("Expanding buffer from " + this.buf.length
107  
-	    // + " to " + (this.buf.length+defaultIncrement));
108  
-	    final byte[] tmp = new byte[super.buf.length + defaultIncrement];
109  
-	    System.arraycopy(super.buf, 0, tmp, 0, super.count);
110  
-	    super.buf = tmp;
111  
-	}
  141
+	ensureCapacity(super.count + 1);
112 142
 	super.buf[super.count++] = b;
113 143
     }
114 144
 
@@ -122,14 +152,7 @@ public void write(final byte b) {
122 152
 
123 153
     @Override
124 154
     public void write(final byte[] buf) {
125  
-	if (super.count + buf.length > super.buf.length) {
126  
-	    // System.err.println("Expanding buffer from " + super.buf.length
127  
-	    // + " to " + (buf.length + super.buf.lengt + defaultIncrement));
128  
-	    final byte[] tmp = new byte[super.buf.length + buf.length
129  
-		    + defaultIncrement];
130  
-	    System.arraycopy(super.buf, 0, tmp, 0, super.count);
131  
-	    super.buf = tmp;
132  
-	}
  155
+	ensureCapacity(super.count + buf.length);
133 156
 	System.arraycopy(buf, 0, super.buf, super.count, buf.length);
134 157
 	super.count += buf.length;
135 158
     }

Showing you all comments on commits in this comparison.

Vlad Dumitrescu

The copyOf method is new in Java 6. To support Java 5 it should be

final byte[] tmp = new byte[super.count];
System.arraycopy(super.buf, 0, tmp, 0, super.count);
super.buf = tmp;
Nico Kruber
Owner

thank you Vlad,
I just updated my branch with this code

Something went wrong with that request. Please try again.