Java Code Skills to Improve Performance

Posted by shahab03 on Thu, 30 May 2019 19:56:13 +0200

Preface

Program performance is directly affected by code quality. This time, I will introduce some tips and practices in coding. Although some seemingly insignificant programming skills can bring about multiple improvements in system performance, it is worth noting.

Use caution exceptions

In Java development, try-catch is often used for error capture, but try-catch statements are very bad for system performance. Although it is impossible to detect the loss of performance in a try-catch, once a try-catch statement is applied to a loop or traversing the body, it will cause great damage to the system performance.

The following is a sample code that applies try-catch to the loop body:

    @Test
    public void test11() {

        long start = System.currentTimeMillis();
        int a = 0;
        for(int i=0;i<1000000000;i++){
            try {
                a++;
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        long useTime = System.currentTimeMillis()-start;
        System.out.println("useTime:"+useTime);

    }

The result of the above code is:

useTime:10

Here's a piece of code that moves try-catch out of the loop and improves performance by nearly half. As follows:

    @Test
    public void test(){
        long start = System.currentTimeMillis();
        int a = 0;
        try {
            for (int i=0;i<1000000000;i++){
                a++;
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        long useTime = System.currentTimeMillis()-start;
        System.out.println(useTime);
    }

Operation results:

useTime:6

Using local variables

The parameters passed when calling the method and the temporary variables created in the call are stored in the Stack, which is fast. Other variables, such as static variables, instance variables, etc., are created in Heap, which is slower.

Here's a section of code that uses local variables to compute:

   @Test
    public void test11() {

        long start = System.currentTimeMillis();
        int a = 0;
        for(int i=0;i<1000000000;i++){
            a++;
        }
        long useTime = System.currentTimeMillis()-start;
        System.out.println("useTime:"+useTime);

    }

Operation results:

useTime:5

Replace local variables with static variables of the class:

    static int aa = 0;
    @Test
    public void test(){
        long start = System.currentTimeMillis();

        for (int i=0;i<1000000000;i++){
            aa++;
        }
        long useTime = System.currentTimeMillis()-start;
        System.out.println("useTime:"+useTime);
    }

Operation results:

useTime:94

From the results of the above two operations, it can be seen that the access speed of local variables is much faster than that of class member variables.

Bit operation instead of multiplication and division

Among all operations, bit operation is the most efficient. Therefore, we can try to use bit operation instead of partial arithmetic operation to improve the speed of the system. The most typical is to optimize the multiplication and division of integers.

Here's a piece of code that uses arithmetic:

    @Test
    public void test11() {

        long start = System.currentTimeMillis();
        int a = 0;
        for(int i=0;i<1000000000;i++){
            a*=2;
            a/=2;
        }
        long useTime = System.currentTimeMillis()-start;
        System.out.println("useTime:"+useTime);
    }

Operation results:

useTime:1451

The multiplication and division operation in the loop body is replaced by the equivalent bit operation. The code is as follows:

    @Test
    public void test(){
        long start = System.currentTimeMillis();
        int aa = 0;
        for (int i=0;i<1000000000;i++){
            aa<<=1;
            aa>>=1;
        }
        long useTime = System.currentTimeMillis()-start;
        System.out.println("useTime:"+useTime);
    }

Operation results:

useTime:10

The last two pieces of code perform exactly the same function, multiplying integers by 2 and dividing them by 2 in each loop. But the running results are very time-consuming, so the efficiency of bit operation is obvious.

Extract expressions

In the process of software development, programmers can easily consciously or unintentionally make code do some "duplicate work". In most cases, due to the high speed of computer operation, these "duplicate work" will not pose too much threat to performance, but if you want to maximize the performance of the system, extracting these "duplicate work" is quite meaningful.

For example, two arithmetic calculations are performed in the following code:

    @Test
    public void testExpression(){
        long start = System.currentTimeMillis();
        double d = Math.random();
        double a = Math.random();
        double b = Math.random();
        double e = Math.random();

        double x,y;
        for(int i=0;i<10000000;i++){
            x = d*a*b/3*4*a;
            y = e*a*b/3*4*a;
        }
        long useTime = System.currentTimeMillis()-start;
        System.out.println("useTime:"+useTime);

    }

Operation results:

useTime:21

Looking carefully, it can be found that the latter half of the two computational expressions are identical, which means that the expressions of the same part are recalculated in each cycle.

After a few improvements, it becomes the following:

    @Test
    public void testExpression99(){
        long start = System.currentTimeMillis();
        double d = Math.random();
        double a = Math.random();
        double b = Math.random();
        double e = Math.random();

        double p,x,y;
        for(int i=0;i<10000000;i++){
            p = a*b/3*4*a;
            x = d*p;
            y = e*p;
        }
        long useTime = System.currentTimeMillis()-start;
        System.out.println("useTime:"+useTime);
    }

Operation results:

useTime:11

Through the operation results, we can see the specific optimization effect.

Similarly, if a time-consuming operation needs to be performed in a certain cycle, the execution result is always unique in the circulatory body, and should be extracted into the circulatory body.

For example, the following code:

for(int i=0;i<100000;i++){
    x[i] = Math.PI*Math.sin(y)*i;
}

The following code should be improved:

//Extracting complex, fixed results of business logic to be processed outside the loop
double p = Math.PI*Math.sin(y);
for(int i=0;i<100000;i++){
    x[i] = p*i;
}

Use arrayCopy()

Array replication is a very frequently used function, and JDK provides an efficient API to implement it.

/**
     * @param      src      the source array.
     * @param      srcPos   starting position in the source array.
     * @param      dest     the destination array.
     * @param      destPos  starting position in the destination data.
     * @param      length   the number of array elements to be copied.
     * @exception  IndexOutOfBoundsException  if copying would cause
     *               access of data outside array bounds.
     * @exception  ArrayStoreException  if an element in the <code>src</code>
     *               array could not be stored into the <code>dest</code> array
     *               because of a type mismatch.
     * @exception  NullPointerException if either <code>src</code> or
     *               <code>dest</code> is <code>null</code>.
     */
    public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

If you need to replicate arrays in your application, you should use this function instead of implementing it yourself.

Here are some examples:

    @Test
    public void testArrayCopy(){
        int size = 100000;
        int[] array = new int[size];
        int[] arraydest = new int[size];

        for(int i=0;i<array.length;i++){
            array[i] = i;
        }
        long start = System.currentTimeMillis();
        for (int k=0;k<1000;k++){
            //Replication
            System.arraycopy(array,0,arraydest,0,size);
        }
        long useTime = System.currentTimeMillis()-start;
        System.out.println("useTime:"+useTime);
    }

Operation results:

useTime:59

Correspondingly, if you implement array replication by yourself in a program, the equivalent code is as follows:

    @Test
    public void testArrayCopy99(){
        int size = 100000;
        int[] array = new int[size];
        int[] arraydest = new int[size];

        for(int i=0;i<array.length;i++){
            array[i] = i;
        }
        long start = System.currentTimeMillis();
        for (int k=0;k<1000;k++){
            for(int i=0;i<size;i++){
                arraydest[i] = array[i];
            }
        }
        long useTime = System.currentTimeMillis()-start;
        System.out.println("useTime:"+useTime);
    }

Operation results:

useTime:102

The effect can be seen from the operation results.

Because the System.arraycopy() function is a native function, the performance of the native function is usually better than that of the ordinary function. For performance reasons only, native functions should be invoked as much as possible during program development.

 

 

 

It will be updated continuously later...

Topics: PHP Programming Java JDK