Bộ sưu tập

Một số lưu ý để cải thiện tốc độ của các đoạn mã Javascript


https://codersontrang.com/2013/02/07/mot-so-luu-y-de-cai-thien-toc-do-cua-cac-doan-ma-javascript/

Javascript là một ngôn ngữ kịch bản mà ở đó ta có thể viết các đoạn mã một cách khá tự do. Cùng phục vụ cho một mục đích nhưng chúng ta có rất nhiều cách khác nhau để viết trên Javascript. Bài viết này sẽ tổng hợp một số trường hợp cần lưu ý khi viết các đoạn mã Javascript và cho thấy sự ảnh hưởng của chúng như thế nào về tốc độ thực hiện các đoạn mã trên bốn trình duyệt phổ biến là Firefox, Safari, Internet Explorer (IE) và Chrome.

#1: Tránh việc gọi hàm quá nhiều

Ta có hai hàm là methodCall()inlinedMethod() cùng thực hiện việc tính tổng các số tự nhiên từ 1 đến 10000 như sau:

 
 function methodCall(){  
      function square(n){return n*n};  
      var i = 10000, sum = 0;  
      while(i--) sum += square(i);  
 }  
 
 function inlinedMethod(){  
      var i = 10000, sum = 0;  
      while(i--) sum += i*i;  
 }  

Việc gọi hàm square() nhiều lần khiến cho thời gian thực hiện hàm methodCall() mất nhiều thời gian hơn khi thực hiện hàm inlinedMethod(). Điều này thể hiện qua bảng thống kê dưới đây

methodCall() inlinedMethod()
codersontrang_browsers_firefox 0.410s 0.150s
codersontrang_browsers_safari 0.056s 0.045s
codersontrang_browsers_ie N/A 0.128s
codersontrang_browsers_chrome 0.027s 0.016s

Đối với IE8, việc gọi hàm quá nhiều lần như trong methodCall() sẽ là nguyên nhân của việc trình duyệt bật lên cảnh báo về sự trì trệ trong tốc độ thực thi và bị đơ.

#2: Khi khai báo một mảng, đối tượng

Khi khai báo một mảng, hoặc đối tượng trong javascript, ta có thể dùng một trong hai cách sau

 
 function classic(){  
      var a = new Array, o = new Object;  
 }  
 
 function literals(){  
      var a = [], o = {};  
 }  

Việc rườm rà trong sử dụng ngôn ngữ trong hàm classic() sẽ làm tăng thời gian thực thi so với hàm literals(), cụ thể như sau

classic() literals()
codersontrang_browsers_firefox 0.291s 0.265s
codersontrang_browsers_safari 0.020s 0.016s
codersontrang_browsers_ie 0.220s 0.185s
codersontrang_browsers_chrome 0.024s 0.010s

#3: Khi sử dụng vòng lặp

Hai đoạn code dưới đây đều thực hiện 60 lần lặp trong việc tăng giá trị của một biến lên một đơn vị

 
 function normalLoop(){  
      var i = 60, j = 0;  
      while(i--) j++;  
 }  
 
 function unrolledLoop(){  
      var j = 0;  
      j++; j++; j++; j++; j++; j++;  
      j++; j++; j++; j++; j++; j++;  
      j++; j++; j++; j++; j++; j++;  
      j++; j++; j++; j++; j++; j++;  
      j++; j++; j++; j++; j++; j++;  
      j++; j++; j++; j++; j++; j++;  
      j++; j++; j++; j++; j++; j++;  
      j++; j++; j++; j++; j++; j++;  
      j++; j++; j++; j++; j++; j++;  
      j++; j++; j++; j++; j++; j++;  
 }  

Thực ra thì cách viết như trong hàm unrolledLoop() ít khi sử dụng trong thực tế, tuy nhiên cái mà ví dụ muốn chỉ ra ở đây là việc sử dụng câu lệnh lặp sẽ làm tăng thêm một khoảng thời gian. Điều này được thể hiện cụ thể như sau:

normalLoop() unrolledLoop()
codersontrang_browsers_firefox 0.023s 0.010s
codersontrang_browsers_safari 0.003s 0.001s
codersontrang_browsers_ie 0.032s 0.015s
codersontrang_browsers_chrome 0.005s 0.001s

#4: Sử dụng cơ chế cache

Cho hai đoạn mã như dưới đây đều sử dụng đối tượng window trong một vòng lặp 10000 lần. Tuy nhiên, một đoạn mã thì ở đó đối tượng window sẽ được cache vào một biến, và trong vòng lặp sẽ gọi ra đối tượng window thông qua biến đó. Đoạn mã còn lại sẽ trực tiếp gọi đến đối tượng window trong vòng lặp.

 
 function cached(){  
      var w = window; i = 10000;  
      while(i--) w.test = 'test';  
 }  
 
 function uncached(){  
      var i = 10000;  
      while(i--) window.test = 'test';  
 }  

Việc gọi trực tiếp quá nhiều lần một đối tượng (window, document…) sẽ làm tăng thời gian thực thi, thể hiện ở bảng dưới đây:

uncached() cached()
codersontrang_browsers_firefox 1.440s 0.825s
codersontrang_browsers_safari 0.07s 0.07s
codersontrang_browsers_ie 2.22s 2.19s
codersontrang_browsers_chrome 0.48s 0.16s

Trong trường hợp trên ta có thể thấy Safari thực thi nhanh vượt trội so với các trình duyệt khác. Ngược lại, IE lại quá là ì ạch.

#5: Sử dụng biểu thức quan hệ

Hai đoạn mã dưới đây là tương tự nhau chỉ khác nhau về thứ tự các toán tử được sử dụng trong biểu thức quan hệ


 var b = false; n = 99;  
 function notTuned(){  
      return n*n && b;  
 }  

 var b = false; n = 99;  
 function tuned(){  
      return b && n*n;  
 }  

Tuy chỉ là đổi chỗ vị trí các toán tử cho nhau, nhưng điều này lại có ảnh hưởng đến tốc độ thực thi. Trong ví dụ trên, n*n tham gia vào quan hệ && luôn trả về giá trị là số nguyên khác 0 và javascript sẽ hiểu đó là true, nếu n*n được đặt trước thì biểu thức quan hệ sẽ phải xét tiếp giá trị của b trước khi đưa ra giá trị cuối cùng của biểu thức. Nhưng ngược lại, nếu b được đặt trước, b luôn trả về là false như vậy biểu thức chắc chắn trả về giá trị false, do đó n*n không cần phải được xét đến nữa và chúng ta sẽ tiết kiệm được khoảng thời gian dùng để thực hiện tính toán giá trị của n*n.
Cụ thể sẽ giống như ở bảng dưới đây

notTuned() tuned()
codersontrang_browsers_firefox 0.005s 0.004s
codersontrang_browsers_safari 0.011s 0.010s
codersontrang_browsers_ie 0.906s 0.391s
codersontrang_browsers_chrome 0.037s 0.021s

#6: Thao tác với đối tượng

Khi thao tác với một đối tượng trong javascript, ta có thể sử dụng từ khóa with hoặc toán tử “.” như sau


 function useWith(){  
      var obj = { prop: 'test', str: ''};  
      with(obj){  
           var i = 10000;  
           while(i--) str += prop;  
           return str;  
      }  
 }  

 function useDot(){  
      var obj = { prop: 'test', str: ''}, i = 10000;  
      while(i--) obj.str += obj.prop;  
      return obj.str;  
 }  

Theo thống kê, việc sử dụng từ khóa with tốn nhiều thời gian hơn việc sử dụng toán tử “.” như bảng dưới đây

useWith() useDot()
codersontrang_browsers_firefox 0.071s 0.012s
codersontrang_browsers_safari 0.039s 0.028s
codersontrang_browsers_ie 0.078s 0.078s
codersontrang_browsers_chrome 0.077s 0.006s

#7: Sử dụng try/catch


 var a = 0;  
 function useTryCatch(){  
      try{  
           a += 1;  
      }catch(e){}  
 }  

 var a = 0;  
 function notUseTryCatch(){  
      a += 1;  
 }  

Việc try/catch sẽ làm tăng thực thi các đoạn mã script

useTryCatch() notUseTryCatch()
codersontrang_browsers_firefox 0.006s 0.005s
codersontrang_browsers_safari 0.287s 0.011s
codersontrang_browsers_ie 0.460s 0.460s
codersontrang_browsers_chrome 0.123s 0.012s

Tuy nhiên thực tế thì có những trường hợp chúng ta bắt buộc phải dùng đến try/catch nếu không chương trình sẽ bị dừng ngay ở đoạn mã lỗi. Vì vậy tùy vào từng trường hợp cụ thể mà chúng ta hãy cân nhắc có thực sự cần thiết phải dùng đến try/catch hay không.

Tốc độ thực thi một đoạn mã Javascript thực sự phụ thuộc vào nhiều yếu tố, nó có thể là do logic khi ta code, do việc ta sử dụng ngôn ngữ (API), hay do chính bản thân các trình duyệt… vì vậy để cải thiện tốc độ một đoạn mã không chỉ đơn giản là làm theo những gì nói trong bài viết này. Bài viết chỉ hy vọng cung cấp cho bạn đọc một số điểm hữu ích mà dựa vào đó ta có khả năng cải thiện được phần nào tốc độ thực thi các đoạn mã Javascript mà thôi.

Good luck!

Nguồn: Extreme Javascript Performance

Advertisements

Trả lời

Mời bạn điền thông tin vào ô dưới đây hoặc kích vào một biểu tượng để đăng nhập:

WordPress.com Logo

Bạn đang bình luận bằng tài khoản WordPress.com Đăng xuất /  Thay đổi )

Google photo

Bạn đang bình luận bằng tài khoản Google Đăng xuất /  Thay đổi )

Twitter picture

Bạn đang bình luận bằng tài khoản Twitter Đăng xuất /  Thay đổi )

Facebook photo

Bạn đang bình luận bằng tài khoản Facebook Đăng xuất /  Thay đổi )

Connecting to %s