CỘNG ĐỒNG CÔNG NGHỆ THÔNG TIN VIỆT NAM

Một số kĩ thuật tối ưu hoá mã nguồn Java

0 Thành viên và 1 Khách đang xem chủ đề.

Đang duyệt NetworkEngineer

  • Administrator
  • Sr. Member
  • *****
    • Bài viết: 469
    • Số Lần: +1/-0
Một số kĩ thuật tối ưu hoá mã nguồn Java

Trong Java việc tối ưu hoá mã nguồn Java là công việc rất quan trọng, nó không chỉ giúp mã nguồn thông thoáng hơn, giúp tiêu tốn ít tài nguyên hệ thống hơn, mà các kĩ thuật được trình bày dưới đây sẽ giúp nâng cao hiệu suất (performance) làm việc của Java khi chạy chương trình!

Một LTV Java có kinh nghiệm luôn coi việc tối ưu hoá mã nguồn như là 1 phần quan trọng của công việc lập trình, trang bị những kĩ thuật, thủ thuật tối ưu sẽ thể hiện một LTV có trình độ, và coi nó như 1 kĩ năng không thể thiếu khi làm việc với Java.

Các kĩ thuật dưới đây không phải là những giải thuật toán học cao siêu, cũng không phải triển khai 1 cách phức tạp, đôi khi chúng rất dễ để thực hiện nhưng rất nhiều LTV không để ý hoặc chưa biết cách để triển khai nó.

I. Các vòng lặp
1. Tránh việc gọi phương thức trong vòng lặp


    Mức độ nghiêm trọng: Cao
    Nguyên tắc: Nếu có thể hãy tránh sử dụng cuộc gọi tới các phương thức như length(), size() ... trong vòng lặp, nó sẽ giúp cải thiện hiệu suất
    Lý do: Việc sử dụng các phương thức trên trong vòng lặp sẽ gây ra rất nhiều cuộc gọi giống nhau và không cần thiết làm tăng xử lý cho chương trình

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Avoid_method_calls_in_loop_violation
{
public void method()
{
        String str = "Hello";
        for (int i = 0; i < str.length(); i++)  // Vi Phạm
        {
          i++;
        }
}
}

Nên được viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Avoid_method_calls_in_loop_correction
{
    public void method()
    {
        String str = "Hello";
        int len = str.length();     // Đẩy size ra bên ngoài vòng lặp
        for (int i = 0; i < len ; i++)
        {
            i++;
        }
 }
}

2. Đưa các tính tóan bất biến ra ngoài vòng lặp

    Mức độ nghiêm trọng: Trung Bình
    Nguyên tắc: Các mã nguồn luôn cho ra một kết quả giống nhau qua từng lần lặp nên được di chuyển ra ngoài vòng lặp
    Lý do: Các tính tóan cho ra kết quả giống nhau, bất biến là không cần thiết phải gọi lại nhiều lần gây ra sự chậm trễ cho chương trình

Ví dụ 1:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Loop_invariant_code_motion_violation
{
    public void method(int x, int y, int[] z)
    {
        for(int i = 0; i < z.length; i++)
        {
            z[i] = x * Math.abs(y);     // Vi phạm
        }
    }
}
Nên được viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Loop_invariant_code_motion_correction
{
    public void method(int x, int y, int[] z)
    {
                    int t1 = x * Math.abs(y);       // Điều chỉnh
        for(int i = 0; i < z.length; i++)
        {
            z[i] = t1;
        }
    }
}
Ví dụ 2:

Mã nguồn: You are not allowed to view links. Register or Login
public NamedElement find(NamedElement namedElement)
{
     for (NamedElement otherNamedElement : getNamedElements()) {
          if (namedElement.getName().equals(otherNamedElement.getName())) { //Vi phạm
               return otherNamedElement;
          }
     }
               return null;
}

Nên viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
public NamedElement find(NamedElement namedElement)
{
     String name = namedElement.getName(); // Chỉnh sửa
     for (NamedElement otherNamedElement : getNamedElements()) {
         if (name.equals(otherNamedElement.getName())) { // Chỉnh sửa

             return otherNamedElement;
         }
     }
             return null;
}

Tham khảo:

    Java Performance Tunning by Jack Shirazi
    The Art of Java Performance Tuning by Ed Merks (Page 20)

3. Tránh nối chuỗi (String) trong vòng lặp

    Mức độ nghiêm trọng: Rất nghiêm trọng
    Nguyên tắc: Sử dụng StringBuffer hoặc StringBuilder thay vì sử dụng nối chuỗi String trong vòng lặp
    Lý do: String là 1 đối tượng bất biến, mỗi khi nối chuỗi gây ra tạo mới đối tượng String, số lần lặp càng lớn càng gây chậm trễ chương trình, tốn tài nguyên xử lý

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class String_concatenation_violation
{
    public void concatValues()
    {
        String result = "";
        for (int i = 0; i < 20; i++)
        {
              result += getNextString();                // Vi phạm
        }
    }
}

Nên được viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class String_concatenation_correction
{
    public void concatValues(String strMainString, String strAppend1, String strAppend2)
    {
        String result = "";
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 20; i++)
 {
     builder.append(getNextString()); // Sử dụng StringBuilder
 }
 result = buffer.toString();
 }
}

Tham khảo: You are not allowed to view links. Register or Login

4. Sử dụng biến tạm thay cho truy cập giá trị của phần tử trong mảng

    Mức độ nghiêm trọng: Cao
    Nguyên tắc: Sử dụng biến tạm để lưu trữ dữ liệu và tính toán thay vì truy cập tới giá trị của phần tử trong mảng
    Lý do: Việc truy cập tới 1 phần tử trong mảng luôn làm tốn chi phí hơn là truy cập tới 1 biến tạm bởi vì máy ảo Java (VM) phải kiểm tra các ràng buộc truy cập tới phần tử đó cũng như phải kiểm tra độ dài của mảng

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
public void setUpArray(int REPEAT) {
     for(int i = 0; i < REPEAT; i++) {
        countArr[0] += 10; // VI PHẠM
     }
}
Nên viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
public void setUpArray(int REPEAT) {
     int tempCound = coundArr[0]; // ĐẨY VÀO BIẾN TẠM
     for(int i = 0; i < REPEAT; i++) {
       tempCound += 10;  // SỬA CHỮA
        countArr[0] = tempCount; // SỮA CHỮA
     }
}

**Tham khảo: **

    Java Performance Tunning 2nd, by Jack Shirazi (Chapter 7 - Page: 194)

5. Sử dụng Int

    Mức độ nghiêm trọng: Trung Bình
    Nguyên tắc: Sử dụng kiểu dữ liệu int cho các chỉ số thay vì sử dụng các kiểu dữ liệu khác
    Lý do: Sử dụng kiểu dữ liệu Int cho các chỉ số là nhanh nhất so với việc sử dụng bất kì kiểu dữ liệu nào khác. VM được tối ưu để sử dụng kiểu Int.

Ví dụ:

Sử dụng int :

Mã nguồn: You are not allowed to view links. Register or Login
        for(int i = 0; i < Repeat; i++)
là nhanh hơn so với sử dụng bất kì kiểu dữ liệu nào khác

Mã nguồn: You are not allowed to view links. Register or Login
     for(long i = 0; i < Repeat; i++)
     for(double i = 0; i < Repeat; i++)
     for(char i = 0; i < Repeat; i++)

Tham khảo:

    Java Performance Tunning 2nd, by Jack Shirazi (Chapter 7 - 7.1.4)

6. Đặt Try Catch ra ngoài vòng lặp

    Mức độ nghiêm trọng: Trung Bình
    Nguyên tắc: Đặt các khối Try/Catch/Finally bên trong vòng lặp có thể làm chậm quá trình thực thi của chương trình

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
import java.io.InputStream;
import java.io.IOException;
class Place_try_catch_out_of_loop_violation
{
    void method (InputStream is)
     {
         int ZERO = 0;
         int TEN = 10;
         int count = 0;

         for (int i = ZERO; i < TEN; i++)
         {
             try            // VIOLATION
             {
                 count += is.read();
             }
             catch (IOException ioe)
             {
                ioe.printStackTrace();
             }
         }
 }
}

Nên viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;

import java.io.InputStream;
import java.io.IOException;
class Place_try_catch_out_of_loop_correction
{
  void method (InputStream is)
     {
         int ZERO = 0;
         int TEN = 10;
         int count = 0;

         try            // Bao Try Catch ra ngoài vòng lặp
         {
             for (int i = ZERO; i < TEN; i++)
             {
                     count += is.read ();
             }
         }
         catch (IOException ioe)
         {
            ioe.printStackTrace();
         }
 }
}

Tham khảo: You are not allowed to view links. Register or Login

II/ Làm việc với chuỗi
1. Sử dụng String.length() để kiểm tra chuỗi rỗng (empty)


    Mức độ nghiêm trọng: Cao
    Nguyên tắc: Sử dụng String.length() để kiểm tra chuỗi rỗng thay vì sử dụng String.equal()
    Lý do: Phương thức String.equals () là quá mức cần thiết để kiểm tra một chuỗi rỗng. Kiểm tra độ dài của chuỗi là 0 sử dụng String.length() sẽ nhanh hơn.

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Use_String_length_to_compare_empty_string_violation
{
    public boolean isDocEmpty()
    {
                return doc.getContents().equals("");        // VI PHẠM
    }
}

Nên viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Use_String_length_to_compare_empty_string_correction
{
    public boolean isDocEmpty()
    {
                return doc.getContents().length() == 0;     // SỬ DỤNG LENGTH
    }
}

Tham khảo:

    You are not allowed to view links. Register or Login
    You are not allowed to view links. Register or Login

2. Sử dụng StringBuffer hoặc StringBuilder (Tham khảo 3. Tránh nối chuỗi (String) trong vòng lặp)
3. Sử dụng equalsIgnoreCase


    Mức độ nghiêm trọng: Cao
    Nguyên tắc: Sử dụng phương thức equalsIgnoreCase() để so sánh chuỗi mà không phân biệt hoa thường

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
public class Use_String_equalsIgnoreCase_violation
{
    public void method()
    {
        String str = "APPPERFECT";
        String str1 = "appperfect";

        if(str1.toUpperCase().equals(str))      // Violation
        {
            System.out.println("Strings are equals");
        }
    }

}

Nên được viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
public class Use_String_equalsIgnoreCase_correction
{
    public void method()
    {
        String str = "APPPERFECT";
        String str1 = "appperfect";

        if(str1.equalsIgnoreCase(str))      // Correction.
        {
            System.out.println("Strings are equals");
        }
    }

}

4. Tránh sử dụng startsWith()

    Mức độ nghiêm trọng: Rất Cao
    Nguyên tắc: Tránh gọi String.startsWith () vì lý do hiệu suất
    Lý do: Trong native mã nguồn Java String.startsWith () xử lý khá nhiều thứ truớc khi cho ra kết quả, vì vậy giới hạn vùng kiểm tra bằng cách kiểm tra ký tự ở vị trí đầu tiên

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Avoid_startsWith_violation
{
    public void method()
    {
        String sTemp="Data";
        if (sTemp.startsWith("D")) // VIOLATION
        {
            sTemp = "data";
        }
    }
}

Nên được viết thành

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Avoid_startsWith_correction
{
    public void method()
    {
        final int ZERO = 0;
        final char D = \'D\';
        String sTemp="Data";

        if (sTemp.length () > ZERO && sTemp.charAt(ZERO) == D)  // CORRECTION
        {
            sTemp = "data";
        }
    }
}

III/ Các trường hợp khác
1. Sử dụng System.arraycopy()


    Nguyên tắc: Sử dụng System.arraycopy() cho việc sao chép mảng
    Lý do: Sử dụng System.arraycopy() là nhanh hơn so với việc sử dụng vòng lặp để sao chép 1 mảng

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Use_System_arrayCopy_violation
{
    public int[] copyArray (int[] array)
    {
        int length = array.length;
        int[] copy = new int [length];
        for(int i=0;i<length;i++)
        {
            copy [i] = array[i];        // VIOLATION
        }
        return copy;
    }
}

Nên viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Use_System_arrayCopy_correction
{
    public int[] copyArray (int[] array)
    {
        final int ZERO = 0;
        int length = array.length;
        int[] copy = new int [length];
        System.arraycopy(array, ZERO, copy, ZERO, length);      // CORRECTION
        return copy;
    }
}

Tham khảo: You are not allowed to view links. Register or Login

2. Tránh lặp lại việc Casting (ép kiểu)

    Mức độ nghiêm trọng: Cao
    Nguyên tắc: Chỉ ép kiểu 1 lần sau đó giữ tham chiếu của nó
    Lý do: Việc lặp đi lặp lại nhiều lần việc ép kiểu khiến việc xử lý chậm trễ hơn

**Ví dụ: **

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;

import java.awt.Component;
import java.awt.TextField;
class Avoid_repeated_casting_violation
{
    public void method(Component comp)
    {
        ((TextField) comp).setText("");     // VIOLATION
        ((TextField) comp).setEditable(false);  // VIOLATION
    }
}

Nên viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;

import java.awt.Component;
import java.awt.TextField;
class Avoid_repeated_casting_correction
{
    public void method(Component comp)
    {
        final TextField tf = (TextField) comp;  // CORRECTION
        tf.setText("");
        tf.setEditable(false);
    }
}
Tham khảo: Java Performance tunning by Jack Shirazi

3. Hủy các cấu trúc if else không cần thiết

    Mức độ nghiêm trọng: Trung Bình
    Nguyên tắc: Đơn giản hóa để nâng cao hiệu quả của mã và giảm kích thước của nó.

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Use_ternary_operator_correction
{
    public boolean test(String value)
    {
        if(value.equals("AppPerfect"))      // VIOLATION
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

Nên viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Use_ternary_operator_correction
{
    public boolean test(String value)
    {
          return value.equals("AppPerfect"); // CORRECTION
        }
 }
4. Luôn sử dụng static với hằng số

    Mức độ nghiêm trọng: Bình thường
    Nguyên tắc: Nên khai báo static với các biến là hằng số

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
public class Always_declare_constant_field_static_violation
{
    final int MAX = 1000; // VIOLATION
    final String NAME = "Noname"; // VIOLATION
}

Nên viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
public class Always_declare_constant_field_static_correction
{
    static final int MAX = 1000; // CORRECTION
    static final String NAME = "Noname"; // VIOLATION
}

5. Tránh check null truớc khi sử dụng so sánh InstanceOf

    Mức độ nghiêm trọng: Trung Bình
    Nguyên tắc: Không nên check NULL truớc khi so sánh InstanceOf
    Lý do: Không cần thiết phải check NULL trong truờwng hợp này, InstanceOf chỉ được so sánh khi đối tượng != NULL, nên việc so sánh là không cần thiết

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
public class Avoid_null_check_before_instanceof_violation
{
    public void method(Object o)
    {
        if(o != null &&  o instanceof Object)       // Violation.
        {
            // Do Something.
        }
    }

}

Nên viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
public class Avoid_null_check_before_instanceof_correction
{
    public void method(Object o)
    {
        if(o instanceof Object)     // Correction
        {
            // Do Something.
        }
    }

}
6. Sử dụng mảng dữ liệu nguyên thủy

    Mức độ nghiêm trọng: Trung Bình
    Nguyên tắc: Sử dụng mảng dữ liệu nguyên thủy thay vì sử dụng Collection
    Lý do: Collection có thể chứa trong nó nhiều kiểu dữ liệu (kiểu object, nguyên thủy ...), vì vậy sử dụng mảng nguyên thủy giúp nâng cao hiệu suất chương trình

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;

import java.util.Vector;
public class Use_array_of_primitive_type_insteadof_collection_violation
{
    public void method()
    {
        Vector vInt = new Vector(); // VIOLATION
        vInt.add(new Integer(1));
        vInt.add(new Integer(2));
        vInt.add(new Integer(3));
    }
}

Nên viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
public class Use_array_of_primitive_type_insteadof_collection_correction
{
    public void method()
    {
        int[] arrInt = new int[3]; // CORRECTION
        arrInt[0] = 1;
        arrInt[1] = 2;
        arrInt[2] = 3;
    }
}
7. Tránh show Debug code

    Mức độ nghiêm trọng: Thấp
    Nguyên tắc: Nên check Debug truớc khi show Log
    Lý do: Việc Debugger vốn bản chất chỉ dành cho nhà phát triển, vì vậy hãy loại bỏ hoặc kiểm tra chúng truớc khi thực thi

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
public void Avoid_debugging_code_violation
{
    private int count =0;
    public int getCount()
    {
        //...
        System.out.println("count ="+count);  // Violation
        return count;
    }
}

Nên viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
public void Avoid_debugging_code_correction
{
    private int count =0;
    public int getCount()
    {
        if (Debug.ON)
        {
            System.out.println("count ="+count);  //correction
        }
        return count;
    }
}

8. Tránh việc sử dụng Thread.sleep()

    Mức độ nghiêm trọng: Cao
    Nguyên tắc: Không nên sử dụng Thread.sleep vì lý do hiệu suất

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Avoid_call_to_Thread_sleep_violation
{
    public void doTest() throws InterruptedException
    {
        //......
        Thread.sleep(100); // VIOLATION
        //......
    }
}
Nên viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Avoid_call_to_Thread_sleep_correction
{
    public void doTest()
    {
        //......
        this.wait(100); // CORRECTION
        //......
    }
}
8. Tránh sử dụng String.charAt()

    Mức độ nghiêm trọng: Cao
    Nguyên tắc: Sử dụng mảng char[] thay vì sử dụng String.charAt();

Ví dụ:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Avoid_using_String_charAt_violation
{
    public void method(String str)
    {
        for(int i=0; i<str.length(); i++)
        {
            System.out.println(str.charAt(i));  // VIOLATION
        }
    }
}

Nên viết thành:

Mã nguồn: You are not allowed to view links. Register or Login
package com.rule;
class Avoid_using_String_charAt_correction
{
    public void method(String str)
    {
        char[] carr = str.toCharArray();        // CORRECTION
        for(int i=0; i<carr.length; i++)
        {
            System.out.println(carr[i]);        // CORRECTION
        }
    }
}

Tham khảo:

    You are not allowed to view links. Register or Login
    You are not allowed to view links. Register or Login

Trong bài này có sử dụng tài liệu tham khảo:

    You are not allowed to view links. Register or Login
    You are not allowed to view links. Register or Login
    Ebook: Java Performance Tunning by Jack Shirazi
    Ebook: The Art of Java Performance Tuning by Ed Merks