Biên dịch C # JIT và .NET


86

Tôi đã trở nên hơi bối rối về chi tiết cách thức hoạt động của trình biên dịch JIT. Tôi biết rằng C # biên dịch xuống IL. Lần đầu tiên nó được chạy, nó là JIT'd. Điều này có liên quan đến việc nó được dịch sang mã gốc không? Thời gian chạy .NET (như một Máy ảo?) Có tương tác với mã JIT'd không? Tôi biết điều này thật ngây thơ, nhưng tôi thực sự bối rối. Ấn tượng của tôi luôn là các hợp ngữ không được diễn giải bởi .NET Runtime nhưng tôi không hiểu chi tiết của tương tác.

Câu trả lời:


83

Có, mã JIT'ing IL liên quan đến việc dịch IL thành các lệnh máy gốc.

Có, thời gian chạy .NET tương tác với mã máy gốc JIT'ed, theo nghĩa là thời gian chạy sở hữu các khối bộ nhớ bị chiếm bởi mã máy gốc, thời gian chạy gọi vào mã máy gốc, v.v.

Bạn đúng rằng thời gian chạy .NET không diễn giải mã IL trong các hội đồng của bạn.

Điều gì xảy ra là khi việc thực thi đạt đến một hàm hoặc khối mã (như, một mệnh đề khác của khối if) chưa được biên dịch JIT thành mã máy gốc, thì JIT'r được gọi để biên dịch khối IL đó thành mã máy gốc . Khi hoàn tất, việc thực thi chương trình nhập mã máy mới được phát ra để thực thi logic chương trình của nó. Nếu trong khi thực thi việc thực thi mã máy gốc đó đạt đến một lệnh gọi hàm đến một hàm chưa được biên dịch thành mã máy, thì JIT'r sẽ được gọi để biên dịch hàm đó "đúng lúc". Và như thế.

JIT'r không nhất thiết phải biên dịch tất cả logic của một thân hàm thành mã máy cùng một lúc. Nếu hàm có câu lệnh if, các khối câu lệnh của mệnh đề if hoặc else có thể không được biên dịch JIT cho đến khi việc thực thi thực sự đi qua khối đó. Các đường dẫn mã chưa được thực thi vẫn ở dạng IL cho đến khi chúng thực thi.

Mã máy gốc đã biên dịch được giữ trong bộ nhớ để nó có thể được sử dụng lại vào lần tiếp theo phần mã đó thực thi. Lần thứ hai bạn gọi một hàm, nó sẽ chạy nhanh hơn lần đầu tiên bạn gọi nó vì không cần bước JIT vào lần thứ hai.

Trong .NET máy tính để bàn, mã máy gốc được lưu trong bộ nhớ suốt thời gian tồn tại của tên miền ứng dụng. Trong .NET CF, mã máy gốc có thể bị loại bỏ nếu ứng dụng sắp hết bộ nhớ. Nó sẽ được biên dịch lại từ mã IL ban đầu trong lần thực thi tiếp theo đi qua mã đó.


14
Chỉnh sửa ngữ nghĩa nhỏ - 'JIT'r không nhất thiết phải biên dịch tất cả logic của một thân hàm' - Trong trường hợp triển khai lý thuyết có thể là như vậy, nhưng trong các triển khai hiện có của thời gian chạy .NET, trình biên dịch JIT sẽ biên dịch toàn bộ tất cả các phương pháp cùng một lúc.
Paul Alexander

18
Về lý do tại sao điều này được thực hiện, chủ yếu là do một trình biên dịch không phải jit không thể giả định rằng các tối ưu hóa nhất định có sẵn trên bất kỳ nền tảng mục tiêu nhất định nào. Các tùy chọn duy nhất là biên dịch theo mẫu số chung thấp nhất hoặc phổ biến hơn là biên dịch nhiều phiên bản, mỗi phiên bản được nhắm mục tiêu vào nền tảng riêng của nó. JIT loại bỏ nhược điểm đó vì bản dịch cuối cùng sang mã máy được thực hiện trên máy đích, nơi trình biên dịch biết những tối ưu hóa nào có sẵn.
Chris Shain

1
Chris, thậm chí còn nghĩ rằng JIT biết những tối ưu hóa nào có sẵn, nên không có thời gian để áp dụng bất kỳ tối ưu hóa quan trọng nào. Chắc NGEN mạnh mẽ hơn.
Grigory

2
@Grigory Có, NGEN có thời gian xa xỉ mà trình biên dịch JIT không có, nhưng có rất nhiều quyết định về codegen mà JIT có thể đưa ra để cải thiện hiệu suất của mã đã biên dịch mà không mất nhiều thời gian để tìm ra. Ví dụ, trình biên dịch JIT có thể chọn các tập lệnh để phù hợp nhất với phần cứng có sẵn được tìm thấy trong thời gian chạy mà không cần thêm thời gian đáng kể cho bước biên dịch JIT. Tôi không nghĩ .NET JITter thực hiện điều này theo bất kỳ cách nào đáng kể, nhưng nó có thể.
dthorpe

24

Mã được "biên dịch" sang Ngôn ngữ Trung cấp của Microsoft, tương tự như định dạng hợp ngữ.

Khi bạn nhấp đúp vào tệp thực thi, Windows sẽ tải tệp mscoree.dllnày sau đó thiết lập môi trường CLR và bắt đầu mã chương trình của bạn. Trình biên dịch JIT bắt đầu đọc mã MSIL trong chương trình của bạn và biên dịch động mã thành các lệnh x86 mà CPU có thể thực thi.


1

.NET sử dụng một ngôn ngữ trung gian gọi là MSIL, đôi khi được viết tắt là IL. Trình biên dịch đọc mã nguồn của bạn và tạo MSIL. Khi bạn chạy chương trình, trình biên dịch .NET Just In Time (JIT) đọc mã MSIL của bạn và tạo ra một ứng dụng thực thi trong bộ nhớ. Bạn sẽ không thấy bất kỳ điều gì trong số này xảy ra, nhưng bạn nên biết điều gì đang xảy ra ở hậu trường.


1

Tôi sẽ mô tả việc biên dịch mã IL thành các hướng dẫn CPU gốc qua ví dụ bên dưới.

public class Example 
{
    static void Main() 
    {
        Console.WriteLine("Hey IL!!!");
    }
}

Về cơ bản CLR biết mọi chi tiết về kiểu và phương thức nào được gọi từ kiểu đó, điều này là do siêu dữ liệu.

Khi CLR bắt đầu thực thi IL thành lệnh CPU gốc thì lúc đó CLR phân bổ cấu trúc dữ liệu bên trong cho mọi kiểu được tham chiếu bởi mã của Main.

Trong trường hợp của chúng tôi, chúng tôi chỉ có một loại Console do đó CLR sẽ cấp phát một cấu trúc dữ liệu nội bộ thông qua cấu trúc bên trong đó, chúng tôi sẽ cấp quyền truy cập vào các loại được tham chiếu

bên trong cấu trúc dữ liệu CLR có các mục nhập về tất cả các phương thức được định nghĩa bởi kiểu đó. Mỗi mục nhập giữ địa chỉ nơi có thể tìm thấy phương thức triển khai.

Khi khởi tạo cấu trúc này CLR đặt mỗi mục nhập trong FUNCTION không có tài liệu chứa bên trong chính CLR Và như bạn có thể đoán FUNCTION này là cái mà chúng ta gọi là JIT Compiler.

Nhìn chung, bạn có thể coi Trình biên dịch JIT như một hàm CLR để biên dịch IL thành các lệnh CPU gốc. Hãy để tôi cho bạn thấy chi tiết quá trình này sẽ như thế nào trong ví dụ của chúng tôi.

1.Khi Main thực hiện lệnh gọi đầu tiên tới WriteLine, hàm JITCompiler được gọi.

Hàm 2.JIT Compiler biết phương thức nào đang được gọi và kiểu nào xác định phương thức này.

3.Sau đó, Trình biên dịch Jit tìm kiếm hợp ngữ đã định nghĩa kiểu đó và lấy mã IL cho phương thức được định nghĩa bởi kiểu đó trong trường hợp mã IL của phương thức WriteLine.

Trình biên dịch 4.JIT phân bổ khối bộ nhớ DYNAMIC , sau đó JIT xác minh và biên dịch mã IL thành mã CPU gốc và lưu mã CPU đó trong khối bộ nhớ đó.

5. Sau đó, trình biên dịch JIT quay trở lại mục nhập cấu trúc dữ liệu bên trong và thay thế địa chỉ (chủ yếu tham chiếu đến việc triển khai mã IL của WriteLine) bằng địa chỉ khối bộ nhớ được tạo động mới có chứa các lệnh CPU gốc của WriteLine.

6.Cuối cùng, hàm JIT Compiler nhảy đến mã trong khối bộ nhớ. Đoạn mã này là sự triển khai của phương thức WriteLine.

7.Sau khi thực hiện WriteLine, mã trở về Mains'code và tiếp tục thực thi như bình thường.


0

.NET Framework sử dụng Môi trường CLR để tạo MSIL (Ngôn ngữ Trung cấp của Microsoft), còn được gọi là IL. Trình biên dịch đọc mã nguồn của bạn và khi bạn xây dựng / biên dịch dự án của mình, Trình biên dịch sẽ tạo MSIL. Bây giờ, khi bạn cuối cùng chạy dự án của mình, .NET JIT ( Trình biên dịch chỉ trong thời gian ) sẽ hoạt động. JIT đọc mã MSIL của bạn và tạo ra mã gốc (là các lệnh x86) mà CPU có thể dễ dàng thực thi. JIT đọc tất cả các lệnh MSIL và thực thi từng dòng một.

Nếu bạn muốn xem, những gì xảy ra đằng sau hậu trường, nó đã được trả lời. Mời các bạn theo dõi - Tại đây

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.