Thread: Performance tweaks

Performance tweaks

From
Ken Geis
Date:
I'm running some tests with PostgreSQL 8.0, JDK 1.5.0, and the CVS head
JDBC driver to see if I can squeeze some performance out of it.

I'm using an old JDBCBench program
(http://developer.mimer.com/features/feature_16.htm) as a test harness.
  It seems this program's major goal is to measure the server's response
to a load, but the JDBC driver will play some part in the measurement.

I was able to get over 5% increase on my setup with a few small changes.

First, in PGStream, I "unrolled" SendInteger4 and SendInteger2, like this:

     private static final byte[] B4 = new byte[4];
     public void SendInteger4(int val) throws IOException
     {
         B4[0] = (byte) ((val >> 24)&255);
         B4[1] = (byte) ((val >> 16)&255);
         B4[2] = (byte) ((val >> 8)&255);
         B4[3] = (byte) (val&255);
         pg_output.write(B4, 0, 4);
     }

Between the two of these, I got a ~4% increase.  The code is slightly
uglier, but it might be worth some performance.  The sneakier (and less
likely to be controversial) one was also in PGStream.  I changed the line

         byte[][] answer = new byte[l_nf][0];
to
         byte[][] answer = new byte[l_nf][];

This gave ~1% increase on the benchmark I was running.


I'll keep you posted if I find anything else to improve.  On your to-do
list, I am really looking forward to the "Allow binary data transfers
for all datatypes not just bytea."


Ken Geis


Re: Performance tweaks

From
Oliver Jowett
Date:
Ken Geis wrote:

> I changed the line
>
>         byte[][] answer = new byte[l_nf][0];
> to
>         byte[][] answer = new byte[l_nf][];
>
> This gave ~1% increase on the benchmark I was running.

Gah?! What JVM? Aren't the two forms equivalent?

-O

Re: Performance tweaks

From
Alan Stange
Date:
Oliver Jowett wrote:

> Ken Geis wrote:
>
>> I changed the line
>>
>>         byte[][] answer = new byte[l_nf][0];
>> to
>>         byte[][] answer = new byte[l_nf][];
>>
>> This gave ~1% increase on the benchmark I was running.
>
>
> Gah?! What JVM? Aren't the two forms equivalent?

No.  They aren't.

The first is l_nf+1 objects being created (and array of byte[] with l_nf
byte[0] entries) and the second is just a single object (an array of
byte[], with null entries).

Any JVM.  It's the language definition.

-- Alan

Re: Performance tweaks

From
Oliver Jowett
Date:
Oliver Jowett wrote:
> Ken Geis wrote:
>
>> I changed the line
>>
>>         byte[][] answer = new byte[l_nf][0];
>> to
>>         byte[][] answer = new byte[l_nf][];
>>
>> This gave ~1% increase on the benchmark I was running.
>
>
> Gah?! What JVM? Aren't the two forms equivalent?

Hmm, after some experimentation, they do produce different bytecode
(multianewarray vs. anewarray):

   public void testit() {
     byte[][] dummy = new byte[10][0];
     byte[][] dummy2 = new byte[10][];
   }

public void testit();
   Code:
    0:   bipush  10
    2:   iconst_0
    3:   multianewarray  #2,  2; //class "[[B"
    7:   astore_1
    8:   bipush  10
    10:  anewarray       #3; //class "[B"
    13:  astore_2
    14:  return

Interesting to know it makes a performance difference. What JVM did you
test on?

-O

"multianewarray" vs. "anewarray" (was: Re: Performance tweaks)

From
Vadim Nasardinov
Date:
On Tuesday 22 February 2005 16:30, Oliver Jowett wrote:
> > I changed the line
> >
> >         byte[][] answer = new byte[l_nf][0];
> > to
> >         byte[][] answer = new byte[l_nf][];
> >
> > This gave ~1% increase on the benchmark I was running.
>
> Gah?! What JVM? Aren't the two forms equivalent?

javap says it's "multianewarray" vs. "anewarray":


 | $ cat Test1.java
 | public class Test1 {
 |     byte[][] answer = new byte[42][0];
 | }
 |
 | $ cat Test2.java
 | public class Test2 {
 |     byte[][] answer = new byte[42][];
 | }
 |
 | $ javap -c -classpath . Test1
 | Compiled from "Test1.java"
 | public class Test1 extends java.lang.Object{
 | byte[][] answer;
 |
 | public Test1();
 |   Code:
 |    0: aload_0
 |    1: invokespecial #1; //Method java/lang/Object."<init>":()V
 |    4: aload_0
 |    5: bipush 42
 |    7: iconst_0
 |    8: multianewarray #2,  2; //class "[[B"
 |    12: putfield #3; //Field answer:[[B
 |    15: return
 |
 | }
 |
 | $ javap -c -classpath . Test2
 | Compiled from "Test2.java"
 | public class Test2 extends java.lang.Object{
 | byte[][] answer;
 |
 | public Test2();
 |   Code:
 |    0: aload_0
 |    1: invokespecial #1; //Method java/lang/Object."<init>":()V
 |    4: aload_0
 |    5: bipush 42
 |    7: anewarray #2; //class "[B"
 |    10: putfield #3; //Field answer:[[B
 |    13: return
 |
 | }

Re: Performance tweaks

From
Oliver Jowett
Date:
Alan Stange wrote:
> Oliver Jowett wrote:
>
>> Ken Geis wrote:
>>
>>>         byte[][] answer = new byte[l_nf][0];
>>> to
>>>         byte[][] answer = new byte[l_nf][];
>>
>> Gah?! What JVM? Aren't the two forms equivalent?
>
> No.  They aren't.
>
> The first is l_nf+1 objects being created (and array of byte[] with l_nf
> byte[0] entries) and the second is just a single object (an array of
> byte[], with null entries).
>
> Any JVM.  It's the language definition.

Ah, right. The Java multidimensional-array stuff always makes my head hurt..

The change seems obviously better, then, since we promptly go and
replace all the top-level array entries with new values.

-O

Re: Performance tweaks

From
Oliver Jowett
Date:
Ken Geis wrote:

> First, in PGStream, I "unrolled" SendInteger4 and SendInteger2, like this:
>
>     private static final byte[] B4 = new byte[4];
>     public void SendInteger4(int val) throws IOException
>     {
>         B4[0] = (byte) ((val >> 24)&255);
>         B4[1] = (byte) ((val >> 16)&255);
>         B4[2] = (byte) ((val >> 8)&255);
>         B4[3] = (byte) (val&255);
>         pg_output.write(B4, 0, 4);
>     }

This isn't safe across multiple PGStreams. Can you try benchmarking
again with "private final" instead of "private static final"? If that's
still a win it is probably worth changing.

-O

Re: Performance tweaks

From
Oliver Jowett
Date:
Ken Geis wrote:
> The sneakier (and less
> likely to be controversial) one was also in PGStream.  I changed the line
>
>         byte[][] answer = new byte[l_nf][0];
> to
>         byte[][] answer = new byte[l_nf][];
>
> This gave ~1% increase on the benchmark I was running.

I have applied this change (and some surrounding rearrangements to avoid
an assignment in the null case, since the initial elements are already
null) to CVS HEAD.

-O

Re: Performance tweaks

From
Ken Geis
Date:
Oliver Jowett wrote:
> Ken Geis wrote:
>
>> First, in PGStream, I "unrolled" SendInteger4 and SendInteger2, like
>> this:
>>
>>     private static final byte[] B4 = new byte[4];
>>     public void SendInteger4(int val) throws IOException
>>     {
>>         B4[0] = (byte) ((val >> 24)&255);
>>         B4[1] = (byte) ((val >> 16)&255);
>>         B4[2] = (byte) ((val >> 8)&255);
>>         B4[3] = (byte) (val&255);
>>         pg_output.write(B4, 0, 4);
>>     }
>
>
> This isn't safe across multiple PGStreams. Can you try benchmarking
> again with "private final" instead of "private static final"? If that's
> still a win it is probably worth changing.

I missed that glaringly obvious point!

My computer is tied up right now in a computational task.  I'll get back
to you tomorrow.


Re: Performance tweaks

From
Ken Geis
Date:
Oliver Jowett wrote:

> Ken Geis wrote:
>
>> First, in PGStream, I "unrolled" SendInteger4 and SendInteger2, like
>> this:
>>
>>     private static final byte[] B4 = new byte[4];
>>     public void SendInteger4(int val) throws IOException
>>     {
>>         B4[0] = (byte) ((val >> 24)&255);
>>         B4[1] = (byte) ((val >> 16)&255);
>>         B4[2] = (byte) ((val >> 8)&255);
>>         B4[3] = (byte) (val&255);
>>         pg_output.write(B4, 0, 4);
>>     }
>
>
> This isn't safe across multiple PGStreams. Can you try benchmarking
> again with "private final" instead of "private static final"? If that's
> still a win it is probably worth changing.

I tested it more rigorously with this change.  Not a win.