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:
- 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à
- 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.
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 :)) –
Related (for V8) http : //www.youtube.com/watch? v = UJPdhx5zTaw –
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