2011-12-30 21 views
7

Mã được biên dịch như C tiêu thụ ít bộ nhớ.Tại sao mã JIT'ed tiêu thụ nhiều bộ nhớ hơn nhiều so với mã được biên dịch hoặc biên dịch?

Mã được giải thích như Python tiêu thụ nhiều bộ nhớ hơn, điều này dễ hiểu.

Với JIT, một chương trình được chọn lọc (được chọn) thành mã máy vào thời gian chạy. Vì vậy, không nên tiêu thụ bộ nhớ của một chương trình JIT'ed được một nơi nào đó giữa một chương trình biên dịch và một giải thích?

Thay vào đó chương trình JIT (chẳng hạn như PyPy) tiêu thụ bộ nhớ nhiều lần nhiều lần so với chương trình diễn giải tương đương (chẳng hạn như Python). Tại sao?

+0

Mọi người đều nói về JIT là nguyên nhân của việc sử dụng bộ nhớ tăng lên trong Pypy, nhưng đó không phải là toàn bộ câu chuyện. Mặc dù một số cấu trúc bộ nhớ của Pypy nhỏ gọn hơn (danh sách tất cả các int), Pypy có nhiều bộ thu gom rác có thể được sử dụng với nó và chắc chắn sẽ ảnh hưởng đến lượng bộ nhớ đang sử dụng. Bộ thu gom rác mặc định hiện tại trong Pypy không tính toán tham chiếu vì lợi ích của tốc độ. Do đó, các đối tượng có thể vẫn còn trong bộ nhớ lâu hơn trong CPython và do đó các chương trình có thể có một bộ nhớ lớn hơn trong Pypy. –

Trả lời

8

Truy tìm trình biên dịch JIT mất nhiều bộ nhớ hơn một chút do thực tế là chúng không cần giữ mã byte cho máy ảo mà còn cả mã máy thực thi trực tiếp. Tuy nhiên, đây chỉ là một nửa câu chuyện.

Hầu hết JIT cũng sẽ lưu giữ nhiều dữ liệu meta về mã byte (và thậm chí cả mã máy) để cho phép chúng xác định những gì cần được JIT'ed và những gì có thể được để lại một mình. Truy tìm JIT (chẳng hạn như LuaJIT) cũng tạo ra các snapshot theo dõi được sử dụng để tinh chỉnh mã tại thời gian chạy, thực hiện những thứ như vòng lặp unrolling hoặc sắp xếp lại nhánh.

Một số cũng lưu trữ các phân đoạn mã thường dùng hoặc bộ đệm tra cứu nhanh để tăng tốc việc tạo mã JIT (LuaJIT thực hiện điều này thông qua DynAsm, nó thực sự có thể giúp giảm mức sử dụng bộ nhớ khi thực hiện chính xác, như trường hợp với dynasm).

Việc sử dụng bộ nhớ phụ thuộc rất nhiều vào công cụ JIT được sử dụng và bản chất của ngôn ngữ mà nó biên dịch (mạnh so với gõ nhẹ). một số JIT sử dụng các kỹ thuật tiên tiến như phân bổ đăng ký dựa trên SSA và phân tích sinh động biến đổi, các loại tối ưu hóa này cũng giúp tiêu thụ bộ nhớ, cùng với những thứ phổ biến hơn như biến vòng lặp.

+1

Ngoài ra, JIT là biên dịch thời gian thực, do đó, nó phải thương mại tối ưu hóa cho tốc độ. Trình biên dịch ngoại tuyến có thể đủ khả năng chi tiêu tối đa 5 giây cho một chức năng. Một JIT không quá nhiều. –

+0

@RaymondChen: hoàn toàn phụ thuộc vào JIT, nếu được viết chính xác, bạn không cần phải giao dịch * tối ưu hóa nhiều, nó chỉ thực sự là kỹ thuật phân tích đắt tiền được tha thứ. – Necrolis

+0

Ngoài ra, JIT thường là * chuyên biên dịch *. Toàn bộ điểm biên dịch "chỉ trong thời gian" là trình biên dịch có thể đợi và xem những gì đang được sử dụng trong thời gian chạy và tối ưu hóa đặc biệt cho điều đó. Có nghĩa là (tùy thuộc vào JIT) có thể có một vài phiên bản khác nhau của mã máy cho một phần mã byte được cung cấp trong bộ nhớ cùng một lúc, cùng với mã byte ban đầu trong trường hợp tình huống mới không khớp bất kỳ phiên bản được biên dịch nào. – Ben

5

Hãy cẩn thận về những gì loại mức sử dụng bộ nhớ mà bạn đang nói đến.

Mã được biên dịch thành C sử dụng bộ nhớ tương đối ít cho mã máy được biên dịch chính nó. Tôi có thể mong đợi Python bytecode cho một thuật toán đã cho thực sự nhỏ hơn mã C đã biên dịch cho một thuật toán tương tự, bởi vì các hoạt động bytecode của Python cao hơn nhiều nên thường có ít hơn để có được một điều nhất định. Nhưng một chương trình Python cũng sẽ có mã biên dịch của trình thông dịch Python trong bộ nhớ, đó là một chương trình khá lớn và phức tạp trong chính nó. Cộng với một chương trình Python điển hình sẽ có nhiều thư viện chuẩn trong bộ nhớ hơn là một chương trình C điển hình (và một chương trình C có thể loại bỏ tất cả các chức năng mà nó không thực sự sử dụng nếu nó được liên kết tĩnh và nếu nó được liên kết động thì nó sẽ chia sẻ mã được biên dịch với bất kỳ quá trình nào khác trong bộ nhớ sử dụng nó).

PyPy sau đó có trên đầu trang của mã máy này của trình biên dịch JIT, cũng như mã máy được tạo ra từ mã byte bytecode (không biến mất, nó cũng phải được giữ lại). Vì vậy, trực giác của bạn (rằng một hệ thống JITed "nên" tiêu thụ bộ nhớ một nơi nào đó giữa một ngôn ngữ biên soạn và một ngôn ngữ hoàn toàn được giải thích) là không chính xác anyway.

Nhưng trên đầu trang của tất cả những người bạn đã có bộ nhớ thực tế được sử dụng bởi các cấu trúc dữ liệu chương trình hoạt động trên. Điều này thay đổi vô cùng, và có rất ít để làm với việc chương trình được biên dịch trước thời hạn, hoặc giải thích, hoặc giải thích và JITed.Một số tối ưu hóa trình biên dịch sẽ làm giảm việc sử dụng bộ nhớ (cho dù chúng được áp dụng trước thời hạn hoặc chỉ trong thời gian), nhưng nhiều người thực sự giao dịch sử dụng bộ nhớ để đạt được tốc độ. Đối với các chương trình xử lý bất kỳ lượng dữ liệu nghiêm trọng nào, nó sẽ hoàn toàn lùn bộ nhớ được sử dụng bởi chính mã đó.

Khi bạn nói:

Thay vào đó một chương trình JIT'ed (như PyPy) tiêu thụ nhiều lần bộ nhớ hơn tương đương với chương trình giải thích (ví dụ như Python). Tại sao?

Bạn đang nghĩ đến chương trình nào? Nếu bạn đã thực sự thực hiện bất kỳ so sánh, tôi đoán từ câu hỏi của bạn rằng họ sẽ được giữa PyPy và CPython. Tôi biết nhiều cấu trúc dữ liệu của PyPy thực sự nhỏ hơn CPython, nhưng lại không liên quan gì đến JIT.

Nếu sử dụng bộ nhớ chi phối của chương trình là mã, thì trình biên dịch JIT sẽ bổ sung thêm chi phí bộ nhớ khổng lồ (cho trình biên dịch và mã được biên dịch) và không thể làm gì nhiều để "giành lại "sử dụng bộ nhớ thông qua tối ưu hóa. Nếu sử dụng bộ nhớ chiếm ưu thế là cấu trúc dữ liệu chương trình, thì tôi sẽ không ngạc nhiên khi thấy PyPy sử dụng bộ nhớ ít hơn đáng kể so với CPython, cho dù JIT có được bật hay không.


Không thực sự là câu trả lời đơn giản cho "Tại sao?" bởi vì các câu trong câu hỏi của bạn không thẳng thắn. Hệ thống nào sử dụng nhiều bộ nhớ hơn phụ thuộc vào nhiều yếu tố; sự hiện diện hay vắng mặt của trình biên dịch JIT là một yếu tố, nhưng nó không phải luôn luôn có ý nghĩa.

+0

Tôi không đồng ý với câu của bạn "Nếu sử dụng bộ nhớ chiếm ưu thế của chương trình là chính mã, thì trình biên dịch JIT thêm phí bộ nhớ khổng lồ ..." - điều này là sai đối với trình biên dịch JIT được thiết kế tốt. Tôi cũng không đồng ý với câu của bạn "Nếu sử dụng bộ nhớ chi phối là cấu trúc dữ liệu chương trình, thì [...] PyPy sử dụng bộ nhớ ít hơn đáng kể so với CPython" - PyPy KHÔNG tốt. –

+0

@Atom Một trình biên dịch JIT dịch mã thành mã khi chạy. Một trình biên dịch JIT chuyên biệt * phải * cũng giữ mã gốc xung quanh (một trình biên dịch JIT không chuyên không phải là cực kỳ thú vị, vì nó có thể được thực hiện như một trình biên dịch trước thời gian, đạt được hiệu quả không phải chạy nó khi chạy và khả năng dành nhiều thời gian hơn cho việc tối ưu hóa). Vì vậy nó phải thêm * ở mức tối thiểu * O (n) không gian trên không, trong đó n là số lượng mã được biên dịch bởi trình biên dịch JIT. Cộng với bộ nhớ của chính JIT ... – Ben

+0

@Atom Đó là sự thật thường JIT không biên dịch hầu hết các chương trình, vì vậy tôi đã có thể phóng đại vụ án. Nhưng bao nhiêu mã của một chương trình được biên dịch JIT sẽ luôn phụ thuộc vào chương trình. Nó chắc chắn là trường hợp mà đối với bất kỳ hệ thống JIT cụ thể nào có các chương trình sẽ trả một lượng đáng kể bộ nhớ trên không so với cùng một chương trình thực thi trong môi trường không phải là JIT. – Ben