2014-09-13 21 views
5

Trong khi đọc tài liệu, tôi đã tìm thấy một tối ưu hóa đơn giản giúp cải thiện hiệu suất javascript.Tối ưu hóa Javascript với `Chức năng mới()`

gốc mã:

function parseRow(columns, parser) { 
    var row = {}; 
    for (var i = 0; i < columns.length; i++) { 
    row[columns[i].name] = parser.readColumnValue(); 
    } 
} 

đang Tối ưu hóa:

var code = 'return {\n'; 
columns.forEach(function(column) { 
    code += '"' + column.name + '":' + 'parser.readColumnValue(),\n'; 
}); 
code += '};\n'; 

var parseRow = new Function('columns', 'parser', code); 

Tìm thấy ở đây: https://github.com/felixge/faster-than-c
Tại sao nó chạy nhanh hơn 20%?
Tôi tin rằng nó loại bỏ tuyên bố for, nhưng không phải là forEach có cùng một chi phí tính toán không?

+0

Có phải nói về mức tăng 20% ​​với logic tạo hàm không? Hoặc với mọi cuộc gọi đến hàm parseRow? Nếu đó là trường hợp thứ hai, tôi cho rằng lợi ích đang xảy ra do sự vắng mặt của vòng lặp trong mã được tối ưu hóa. (Trong trường hợp đầu tiên, cho vòng lặp sẽ chạy với mọi cuộc gọi đến hàm parseRow. Ngoài ra nhiều cuộc gọi đến cột.length tài sản cũng có thể được quy cho sự chậm chạp của mã ban đầu. Chỉ cần 2 xu của tôi :)) –

+1

Related (for V8) http : //www.youtube.com/watch? v = UJPdhx5zTaw –

+1

khi kích thước của 'cột' đủ lớn, 'mã được tối ưu hóa' dường như không nhanh hơn. xem [jsPerf] (http://jsperf.com/javascript-optimization-with-new-function) – rhgb

Trả lời

5

Sự khác biệt là bạn chỉ sử dụng forEach để xây dựng hàm được tối ưu hóa. Khi hàm được tạo, không có bất kỳ vòng lặp nào bên trong: các tên loop is unrolled và cột là hardcoded. Phương pháp này sau đó được eval chỉnh sửa thành chức năng hoạt động, thậm chí có thể được biên dịch thành mã máy, depending on the engine. Điều này dẫn đến hai cải tiến hiệu suất:

  1. Bằng cách loại bỏ việc kiểm tra for vòng lặp điều kiện (i < columns.length) hoàn toàn, không có phân nhánh, và
  2. By hardcoding giá trị của column[i].name thành nhiều báo cáo, bạn bỏ đánh giá column[i] và tra cứu để column.name trong mỗi bước.

Vì vậy, sau khi gọi new Function(...) với mã thông qua như là một String, biến parseRow bạn được tham chiếu đến các chức năng sau:

function parseRow(columns, parser) { 
    return { 
     "columnOne": parser.readColumnValue(), 
     "columnTwo": parser.readColumnValue(), 
     "columnThree": parser.readColumnValue(), 
     ... 
    }; 
} 

Lưu ý rằng không có bất kỳ vòng lặp, phân nhánh, hoặc tra cứu khác trong mã đó, ngoại trừ nhiều cuộc gọi parser.readColumnValue().

Tại sao điều này có thể xảy ra trong JavaScript?

Lý do tại sao tính năng này hoạt động hiệu quả trong JavaScript là do mã nguồn JavaScript trong bất kỳ trang web nào cũng cần được công cụ JS giải thích hoặc biên soạn. Bạn không gửi trang web của mình với các tệp thi hành đã biên dịch hoặc thậm chí (phần nào) được mã hóa bytecode (như Java hoặc .NET).Mỗi lần một tệp mới .js được tải, trình duyệt của bạn sẽ biên dịch nó từ đầu trước khi chạy nó (tốt, chính xác, trong các công cụ hiện đại, đó là điều gì đó giữa diễn giải và biên dịch, tức là JITting).

Điều này có nghĩa là việc tạo một hàm làm việc từ chuỗi (tức là biên dịch mã) trong thời gian chạy không kém hiệu quả so với việc viết mã viết tay từ tệp .js. So sánh điều đó với chương trình C/C++, (trong tất cả các trường hợp hợp lý) được biên dịch thành mã máy (tức là tệp thực thi gần với CPU như bạn có thể nhận được) before it reaches the customer.

Nếu bạn muốn làm điều này trong C++ (một loại self-modifying code), bạn sẽ phải đóng gói trình biên dịch dọc theo ứng dụng của bạn để xây dựng mã, và chi phí xây dựng chức năng này sẽ thừa cân lợi ích bạn sẽ nhận được khi cuối cùng bạn sẽ bắt đầu nó. Trong .NET, ví dụ, nó cũng không phải là bất thường đối với một chương trình là emit methods or even assemblies at run time, sau đó nhận được JIT được biên dịch thành mã máy cho phép cải thiện hiệu suất tiềm năng, chẳng hạn như câu hỏi trong câu hỏi của bạn.

2

Mức tăng hiệu suất phụ thuộc rất nhiều vào công cụ JavaScript, cũng như trên dữ liệu đang được xử lý. Chúng tôi không biết chính xác hoàn cảnh "nhanh hơn 20%" (ngoại trừ việc sử dụng node.js). Nó có thể chậm hơn trong một số tình huống. (Chỉnh sửa: Bạn cần phải gọi hàm thường đủ để vượt quá chi phí xây dựng). Một số lý do có thể có cho lợi ích:

Mã được tối ưu hóa tạo đối tượng theo nghĩa đen. Phiên bản trước đó liên tục gán giá trị cho các thuộc tính chưa có sẵn. Điều đó có một số chi phí liên quan đến nó.

row[columns[i].name] có ba lần tra cứu, trong khi phiên bản được tối ưu hóa không có, khi chức năng được tạo. Và đừng quên rằng row[columns[i].name] không tồn tại, vì vậy việc tra cứu là tốn kém hơn. columns.length cũng là một tra cứu.

Các vấn đề liên quan