Một số tips tối ưu tốc độ khi làm việc với array trong javascript

Giống như các ngôn ngữ lập trình khác, array trong javascript thực sự rất hữu dụng và giúp chúng ta rất nhiều trong quá trình làm việc. Tuy nhiên, vẫn còn có những thứ chúng ta có thể tối ưu được để có thể khiến cho mã javascript của mình tốt hơn. Dưới đây là một số tối ưu chúng ta có thể thực hiện:

1. Sử dụng [] thì tốt hơn {}

Bất kỳ khi nào có thể, nên sử dụng [] thay cho {}. Thực tế thì javascript object được sử dụng rất rộng rãi vì tính đa năng và khả năng sử dụng linh hoạt của nó. Tuy vậy đối với các object đơn giản thì chúng ta có thể sử dụng mảng để thay thế, với mảng trong js thì thứ duy nhất được dùng làm key chính là số nguyên. Một mảng trong js chắc chắn sẽ tốt hơn về hiệu suất so với object trong tất cả các tác vụ (get, set, computations...)

2. [ integer ] tốt hơn là [ string ]

Cùng quan điểm với (1), khi làm việc với object mà key chỉ sử dụng là số nguyên thì phần lớn các javascript engine sẽ hiểu đó là một mảng số nguyên và thực thi nhanh hơn nhiêu

3. Không dùng vòng lặp lùi với mảng

Có thể bạn từng nhìn thấy một cách để lặp qua các phần tử mảng như thế này

var i = myArray.length;
while ( i-- ) {
     //process myArray[i] ...
}

Nhìn có vẻ tối ưu hơn vì bạn không phải thực hiện thêm một lần test xem quá trình lặp đã xong chưa. Thực tế sẽ không như vậy bởi vì cách làm này sẽ bỏ qua toàn bộ cpu caches, toàn bộ vòng lặp của bạn sẽ chiếm gấp đôi thời gian so với một vòng for thông thường

var i=0, len=myArray.length, _index = -1;
while (i != len) {
    if (myArray[i] == value) { _index=i ; }
    i++;
}

4. Không bao giờ thay đổi array’s size

Có một điều quan trọng chúng ta nên chú ý, nó ẩn chứa sau những phương thức native như push, pop, slice... Đó là mỗi khi chúng ta thực hiện thay đổi array's size sẽ dẫn đến việc thực hiện một trong các hành động đối với memory như: allocation, fragmentation, copy (cấp phát, phân mảnh, sao chép). Ví dụ:

// khởi tạo 3 mảng : 
var A = [5,6], B = [1,2,3], C = [ 1, 2, 4 ];   
// thêm phần tử vào mảng B :
B.push (1);  
// pop một phần tử ra khỏi C : 
C.pop();

3 dòng code trên sẽ được xử lý như sau:

Chỉ với một ví dụ rất đơn giản này: 1 push, 1 pop, chúng ta có thể thấy là có khá nhiều hoạt động liên quan đến memory: 3 lượt cấp phát bộ nhớ, 3 lượt thu hồi, 8 lượt sao chép giá trị. Hãy tưởng tượng bạn đang làm việc với một mảng các mảng (mảng nhiều chiều), với rất nhiều các thao tác nhỏ trên mảng, thì vấn đề này thực sự trở nên quan trọng. Push và Pop operation không luôn luôn thực thi với cùng một khoảng thời gian mà thời gian thực thi tỉ lệ thuận với array size O(n) và quá trình phân mảnh bộ nhớ Thậm chí không phải thông qua PUSH hoặc POP, bạn có thể đổi array size một cách trực tiếp như sau

myArray.length = 12;

tuy nhiên thay đổi array size thế này thì các bước thực hiện đối với bộ nhớ cũng không khác gì đối với PUSH và POP cả (về performance) Từ ví dụ trên thì chúng ta có thể tối ưu cách dùng array bằng cách Sử dụng Fixed sized arrray bất kỳ khi nào có thể Sử dụng Over-sized array thì tốt hơn là thay đổi array size* (4-2) Để sử dụng over-sized array (theo 4-2) chúng ta có thể modify code một chút như sau

var B = [ 0, 0, 0, 0, 0 ] ;    // khởi tạo mảng B size 5 và fill bởi 0.
var BLength = 0 ;
// thự hiện push
B[ BLength++ ] = 1;
// thực hiện pop
var last = B[ --BLength  ] ;

Ví dụ trên yêu cầu 1 read, 1 write, và 2 add Không có sự phân bổ bộ nhớ, không copy. Trong trường hợp bạn cần kiểm tra array khi pop thì hiệu suất vẫn sẽ tốt hơn rất nhiều

5. Một số lưu ý khi làm việc với array

  • Trong Javascript, chúng ta không thể xác định được khi nào thì Garbage Collector sẽ chạy, vì vậy việc test performance đối với các điểm (1-4) là khá khó khăn
  • Khi làm việc với một array duy nhất thì dù sử dụng PUSH hay POP thì cũng sẽ không có các hành động cấp phát, thu hồi hay sao chép sảy ra đối với bộ nhớ
  • Khởi tạo, cấp phát array bất kỳ khi nào có thể
  • Việt lại các phương thức PUSH, POP để sử dụng:
    • Push: myArray[myArray.length] = XX
    • Pop: var last=myArray[myArray.length--]
    • Unshift:
var unshiftArray = function (arr, item) {
    var len=arr.length;
    while (len) {
        arr[len] = arr[len-1];
        len--;
    }
    arr[0] = item; 
};

Indexof:

function(arr, item) {
    for (var i=0, len=arr.length; i!=len ; i++) {
        if (arr[i] === item) { return i }
    }
    return -1;
};

LastIndexOf:

function(arr, item) {
    var i=arr.length;
    while (i--) { 
        if (arr[i] === item) { break } }
        return i;
    };
}

Tham khảo thêm https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array https://gamealchemist.wordpress.com/2013/05/01/lets-get-those-javascript-arrays-to-work-fast/ http://output.jsbin.com/ugeloz/2 https://jsperf.com/push-allocated-vs-dynamic https://gamealchemist.wordpress.com/2013/02/02/no-more-garbage-pooling-objects-built-with-constructor-functions/ https://gamealchemist.wordpress.com/2016/04/15/writing-efficient-javascript-a-few-tips/

All Rights Reserved