2010-10-03 39 views
9

Tôi đang cố gắng hiểu sự khác biệt thực tế trong quá trình thực hiện chương trình trong Giải mã và công văn giải thích và diễn giải luồng.Giải mã và công văn giải thích vs Giải thích luồng

Ví dụ về cả hai sẽ thực sự hữu ích.

Tôi hiểu cách Java bytecode hoạt động và cách hoạt động của ngôn ngữ assembly. Nhưng DDI và TI phù hợp ở đâu?

Bối cảnh: Virtual machines: versatile platforms for systems and processes

Trả lời

17

(Lưu ý: Tôi sẽ giả định rằng bằng cách "giải mã và công văn" bạn có nghĩa là một thông dịch viên chuyển đổi dựa trên.)

Sự khác biệt giữa một công tắc dựa trên và một thông dịch viên tại ren thời gian chạy, về cơ bản, số lần nhảy được thực hiện.

Trong trình thông dịch dựa trên chuyển mạch, hướng dẫn được giải mã tại một số vị trí trung tâm và dựa trên kết quả giải mã, bước nhảy được thực hiện cho đoạn mã xử lý lệnh đã giải mã. Khi đoạn mã đó đã hoàn thành việc giải thích lệnh, nó nhảy trở lại mã giải mã tập trung, mã này sẽ tiếp tục với lệnh tiếp theo. Điều này có nghĩa là (ít nhất) hai bước nhảy được thực hiện theo hướng dẫn diễn giải. Các đoạn mã sau C minh họa những gì một thông dịch viên như vậy có thể trông giống như:

typedef enum { 
    add, /* ... */ 
} instruction_t; 

void interpret() { 
    static instruction_t program[] = { add /* ... */ }; 
    instruction_t* pc = program; 
    int* sp = ...; /* stack pointer */ 
    for (;;) { 
    switch (*pc++) { 
     case add: 
     sp[1] += sp[0]; 
     sp++; 
     break; 
     /* ... other instructions */ 
    } 
    } 
} 

Trong một thông dịch viên ren, mã giải mã là không tập trung, mà đúng hơn là nhân đôi ở phần cuối của mỗi đoạn mã để xử lý một lệnh. Điều này có nghĩa rằng một khi một lệnh đã được giải thích, thay vì nhảy trở lại một số mã giải mã tập trung, trình thông dịch sẽ giải mã lệnh tiếp theo và ngay lập tức nhảy vào nó. Việc triển khai mã luồng hiệu quả trong ANSI-C là không thực sự có thể, nhưng phần mở rộng "tính toán goto" của GCC hoạt động rất tốt cho điều đó. Dưới đây là một phiên bản ren của người phiên dịch theo thời gian:

void interpret() { 
    void* program[] = { &&l_add, /* ... */ }; 
    int* sp = ...; 
    void** pc = program; 
    goto **pc; /* jump to first instruction */ 
l_add: 
    sp[1] += sp[0]; 
    ++sp; 
    goto **(++pc); /* jump to next instruction */ 
    /* ... other instructions */ 
} 

Ngoài việc tiết kiệm một bước nhảy, Biên dịch ren như vậy cũng là hiệu quả hơn vì nhảy gián tiếp lặp lại (để chỉ lệnh kế tiếp) có thể được dự đoán tốt hơn bằng cách CPU hiện đại. Anton Ertl có một số giấy tờ thú vị trên his home page, đặc biệt là cái gọi là "Cấu trúc và hiệu suất của các phiên dịch hiệu quả", từ đó các đoạn mã trên đã được điều chỉnh.