"Kevin O'Neill" <kevin@rocketred. To: FOP Developers <[EMAIL PROTECTED]> com.au> cc: (bcc: Thomas Seremet/GCR-NAmerica/GRN) Subject: Re: A performance patch for PDFInfo class 11/13/2002 05:41 AM Please respond to fop-dev
><snip/> > >> Some more insight or confusion. The byte code maybe similar in the sense >> that String uses "".concat() and >> StringBuffer uses new StringBuffer().append to do their individual >> concatenations but the way they are >> treated by the JVM is not the same. Of course not all JVM's are created >> equal but Strings are stored as constants thus you see the ldc opcode when >> creating Strings. Even though a String holds on to an internal >> character array as does a StringBuffer, a String creates a new String when >> it is concatentating another String to itself NOT modifing its internal >> character array. On the other hand a StringBuffer actually modifies its >> internal character array to represent the new String though this is >> accomplished by increasing the array size by creating a new char array. >> This is only part of the story since there is a difference between Strings >> stored in the so-called constants pool and new Strings created during >> runtime which do not go into the constants-pool automatically. Very good >> info concerning this on the web. > >Okay firstly, please look at the pcode generated earlier in this thread. >It was generated by the moder compiler on jdk 1.4.0 the string addition >is indeed concatinated using StringBuffer.append() > > public String testStringBufferChained() > { > return (new StringBuffer().append("this ") > .append(makeString("is ")) > .append("a ") > .append(makeString("test"))).toString(); > } > > public String testStringAdd() > { > return > "this " > + makeString("is ") > + "a " > + makeString("test"); > } > >becomes ... > >Method java.lang.String testStringBufferChained() > 0 new #2 <Class java.lang.StringBuffer> > 3 dup > 4 invokespecial #3 <Method java.lang.StringBuffer()> > 7 ldc #4 <String "this "> > 9 invokevirtual #5 <Method java.lang.StringBuffer >append(java.lang.String)> > 12 aload_0 > 13 ldc #6 <String "is "> > 15 invokespecial #7 <Method java.lang.String >makeString(java.lang.String)> > 18 invokevirtual #5 <Method java.lang.StringBuffer >append(java.lang.String)> > 21 ldc #8 <String "a "> > 23 invokevirtual #5 <Method java.lang.StringBuffer >append(java.lang.String)> > 26 aload_0 > 27 ldc #9 <String "test"> > 29 invokespecial #7 <Method java.lang.String >makeString(java.lang.String)> > 32 invokevirtual #5 <Method java.lang.StringBuffer >append(java.lang.String)> > 35 invokevirtual #10 <Method java.lang.String toString()> > 38 areturn > >Method java.lang.String testStringAdd() > 0 new #2 <Class java.lang.StringBuffer> > 3 dup > 4 invokespecial #3 <Method java.lang.StringBuffer()> > 7 ldc #4 <String "this "> > 9 invokevirtual #5 <Method java.lang.StringBuffer >append(java.lang.String)> > 12 aload_0 > 13 ldc #6 <String "is "> > 15 invokespecial #7 <Method java.lang.String >makeString(java.lang.String)> > 18 invokevirtual #5 <Method java.lang.StringBuffer >append(java.lang.String)> > 21 ldc #8 <String "a "> > 23 invokevirtual #5 <Method java.lang.StringBuffer >append(java.lang.String)> > 26 aload_0 > 27 ldc #9 <String "test"> > 29 invokespecial #7 <Method java.lang.String >makeString(java.lang.String)> > 32 invokevirtual #5 <Method java.lang.StringBuffer >append(java.lang.String)> > 35 invokevirtual #10 <Method java.lang.String toString()> > 38 areturn > > >So once again I say these are these are identical, the underlying VM has >no idea of any difference. I would though contend that the String >addition is easier to read. I know I also came in late into this thread. I would like to say I am very excited about the possibilities of FOP in the future and the already realized gains FOP gives. My overall grasp of FOP at this moment is still limited but I have been delving into the code and using it for a current project for the past 2 months. In no way am I trying to be insulting to anyone. I realize everyone here is very well versed in various areas of programming. Performance has always been an interest to me in general so I enjoy these types of topics. I don't mind being proven wrong. Yes. You are correct on your assumptions based on your code. Maybe I should have been more clear and read the initial beginning to this thread but I would like to clear two points up, my apologies. I was trying to refer to two seperate issues that were being discussed, more so to what happens during runtime then compile time. The first point was what the String and StringBuffer java code looks in the pcode. String does do concantenation with the concat method and StringBuffer with append?. It does do that except under certain conditions. So, what are the conditions under the + operator? -If there is a chain of anonymous strings being initialized to a variable a LOAD happens. -If there is 1 reference to a String at the head or end of a chain of anonymous Strings a String.concat happens -If there are only 2 references to a String(s) being "concantenated using + operator" a String.concat happens -If the chain contains 2 or more anonymous strings seperated by at least 1 reference to a String StringBuffer.append happens -If the chain contains 3 or more references to a String(s) StringBuffer.append happens -If the chain contains at least 2 references to a String(s) and at least 1 anonymous String StringBuffer.append happens What are the conditions for += operator? -They are the same as above except for the additional concantenation that happens implicitly to the variable adding to itself. So to the statement that StringBuffer.append happens in "String addition" is only partly true given String.concat happens in a subset of the cases. Code below JDK 1.4.0_01-b03: public Test() { String a = "foo"; a += "bar"; String b = "so" + "far" + "so" + "good"; String c = "boo" + "far" + a; String d = b + c; String e = a + b + c; String f = makeString("goo") + makeString("bar"); String g = "boo" + a + "far"; d += e + f; d += "foo"+"bar"+"foo"; d += "boo" + "bar" + e; d += "boo" + e + "bar"; String h = b + b; String x = b + c + "end"; } public Test() { // 0 0:aload_0 // 1 1:invokespecial #1 <Method void Object()> String a = "foo"; // 2 4:ldc1 #3 <String "foo"> // 3 6:astore_1 a = String.valueOf(String.valueOf(a)).concat("bar"); // 4 7:aload_1 // 5 8:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 6 11:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 7 14:ldc1 #5 <String "bar"> // 8 16:invokevirtual #6 <Method java.lang.String java.lang.String.concat(java.lang.String)> // 9 19:astore_1 String b = "sofarsogood"; // 10 20:ldc1 #7 <String "sofarsogood"> // 11 22:astore_2 String c = "boofar".concat(String.valueOf(String.valueOf(a))); // 12 23:ldc1 #8 <String "boofar"> // 13 25:aload_1 // 14 26:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 15 29:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 16 32:invokevirtual #6 <Method java.lang.String java.lang.String.concat(java.lang.String)> // 17 35:astore_3 String d = String.valueOf(b) + String.valueOf(c); // 18 36:aload_2 // 19 37:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 20 40:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 21 43:aload_3 // 22 44:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 23 47:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 24 50:invokevirtual #6 <Method java.lang.String java.lang.String.concat(java.lang.String)> // 25 53:astore 4 String e = String.valueOf(String.valueOf((new StringBuffer(String.valueOf(String.valueOf(a)))).append(b).append(c))); // 26 55:new #9 <Class java.lang.StringBuffer> // 27 58:dup // 28 59:aload_1 // 29 60:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 30 63:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 31 66:invokespecial #10 <Method void StringBuffer(java.lang.String)> // 32 69:aload_2 // 33 70:invokevirtual #11 <Method java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String)> // 34 73:aload_3 // 35 74:invokevirtual #11 <Method java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String)> // 36 77:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 37 80:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 38 83:astore 5 String f = String.valueOf(makeString("goo")) + String.valueOf(makeString("bar")); // 39 85:aload_0 // 40 86:ldc1 #12 <String "goo"> // 41 88:invokespecial #13 <Method java.lang.String tester.Test.makeString(java.lang.String)> // 42 91:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 43 94:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 44 97:aload_0 // 45 98:ldc1 #5 <String "bar"> // 46 100:invokespecial #13 <Method java.lang.String tester.Test.makeString(java.lang.String)> // 47 103:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 48 106:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 49 109:invokevirtual #6 <Method java.lang.String java.lang.String.concat(java.lang.String)> // 50 112:astore 6 String g = String.valueOf(String.valueOf((new StringBuffer("boo ")).append(a).append("far"))); // 51 114:new #9 <Class java.lang.StringBuffer> // 52 117:dup // 53 118:ldc1 #14 <String "boo"> // 54 120:invokespecial #10 <Method void StringBuffer(java.lang.String)> // 55 123:aload_1 // 56 124:invokevirtual #11 <Method java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String)> // 57 127:ldc1 #15 <String "far"> // 58 129:invokevirtual #11 <Method java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String)> // 59 132:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 60 135:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 61 138:astore 7 d = String.valueOf(d) + String.valueOf(String.valueOf(e) + String.valueOf(f)); // 62 140:aload 4 // 63 142:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 64 145:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 65 148:aload 5 // 66 150:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 67 153:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 68 156:aload 6 // 69 158:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 70 161:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 71 164:invokevirtual #6 <Method java.lang.String java.lang.String.concat(java.lang.String)> // 72 167:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 73 170:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 74 173:invokevirtual #6 <Method java.lang.String java.lang.String.concat(java.lang.String)> // 75 176:astore 4 d = String.valueOf(String.valueOf(d)).concat("foobarfoo"); // 76 178:aload 4 // 77 180:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 78 183:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 79 186:ldc1 #16 <String "foobarfoo"> // 80 188:invokevirtual #6 <Method java.lang.String java.lang.String.concat(java.lang.String)> // 81 191:astore 4 d = String.valueOf(d) + String.valueOf("boobar ".concat(String.valueOf(String.valueOf(e)))); // 82 193:aload 4 // 83 195:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 84 198:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 85 201:ldc1 #17 <String "boobar"> // 86 203:aload 5 // 87 205:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 88 208:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 89 211:invokevirtual #6 <Method java.lang.String java.lang.String.concat(java.lang.String)> // 90 214:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 91 217:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 92 220:invokevirtual #6 <Method java.lang.String java.lang.String.concat(java.lang.String)> // 93 223:astore 4 d = String.valueOf(d) + String.valueOf(String.valueOf(String.valueOf((new StringBuffer("boo ")).append(e).append("bar")))); // 94 225:aload 4 // 95 227:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 96 230:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 97 233:new #9 <Class java.lang.StringBuffer> // 98 236:dup // 99 237:ldc1 #14 <String "boo"> // 100 239:invokespecial #10 <Method void StringBuffer(java.lang.String)> // 101 242:aload 5 // 102 244:invokevirtual #11 <Method java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String)> // 103 247:ldc1 #5 <String "bar"> // 104 249:invokevirtual #11 <Method java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String)> // 105 252:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 106 255:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 107 258:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 108 261:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 109 264:invokevirtual #6 <Method java.lang.String java.lang.String.concat(java.lang.String)> // 110 267:astore 4 String h = String.valueOf(b) + String.valueOf(b); // 111 269:aload_2 // 112 270:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 113 273:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 114 276:aload_2 // 115 277:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 116 280:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 117 283:invokevirtual #6 <Method java.lang.String java.lang.String.concat(java.lang.String)> // 118 286:astore 8 String x = String.valueOf(String.valueOf((new StringBuffer(String.valueOf(String.valueOf(b)))).append(c).append("end"))); // 119 288:new #9 <Class java.lang.StringBuffer> // 120 291:dup // 121 292:aload_2 // 122 293:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 123 296:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 124 299:invokespecial #10 <Method void StringBuffer(java.lang.String)> // 125 302:aload_3 // 126 303:invokevirtual #11 <Method java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String)> // 127 306:ldc1 #18 <String "end"> // 128 308:invokevirtual #11 <Method java.lang.StringBuffer java.lang.StringBuffer.append(java.lang.String)> // 129 311:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 130 314:invokestatic #4 <Method java.lang.String java.lang.String.valueOf(java.lang.Object)> // 131 317:astore 9 // 132 319:return } The point that is more interesting is what happens during runtime and that's what I was trying to emphasize, poorly. The question is how many String objects are being handled by the JVM in memory, how often garbage collection happens, and at what cost? I have not timed your examples but I believe what you stated. I have run similar tests but from a different angle. The only difference between tests was that they were controlled within a loop. The times were different. The reason I did this is to see how much memory and time is spent within the scope of each method. The testStringAppendloop was exponentially faster with testStringAddloop coming in second and third for testStringIncrementloop. The times of course will differ on different platforms and cpu's but I list them anyway. This is my sample code derived from your examples; byte-code following. 1.4.0_01-b03 times with 3000 iterations compaq 512 mb 866ghz intel cpu: 16 ms testStringAppendloop 2000 ms testStringAddloop 5531 ms testStringIncrementloop public String testStringIncrementloop(int loop) { String temp = ""; for (int i=0;i<loop;i++) { temp += makeString("this is "); temp += "a "; temp += makeString("concat test"); } return temp; } public String testStringAddloop(int loop) { String temp = ""; for (int i=0;i<loop;i++) { temp = temp + makeString("this is ") + "a " + makeString("concat test"); } return temp; } public String testStringAppendloop(int loop) { StringBuffer temp = new StringBuffer(""); for (int i=0;i<loop;i++) { temp.append(makeString("this is ")).append("a ").append(makeString("concat test")); } return temp.toString(); } -Tom --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, email: [EMAIL PROTECTED]