Trình biên dịch có sử dụng đa luồng cho thời gian biên dịch nhanh hơn không?


16

Nếu tôi nhớ khóa học trình biên dịch của mình một cách chính xác, trình biên dịch điển hình có phác thảo đơn giản hóa sau:

  • Một bộ phân tích từ vựng quét (hoặc gọi một số chức năng quét trên) theo từng ký tự mã nguồn
  • Chuỗi ký tự đầu vào được kiểm tra theo từ điển của các từ vựng về tính hợp lệ
  • Nếu từ vựng hợp lệ, thì nó được phân loại là mã thông báo tương ứng với
  • Trình phân tích cú pháp xác thực cú pháp của sự kết hợp các mã thông báo; token-by-thẻ .

Về mặt lý thuyết có khả thi để chia mã nguồn thành các phần tư (hoặc bất kỳ mẫu số nào) và đa luồng quá trình quét và phân tích cú pháp không? Trình biên dịch có tồn tại mà sử dụng đa luồng không?




1
@RobertHarvey Câu trả lời hàng đầu của liên kết đầu tiên đã viết, "nhưng bản thân các trình biên dịch vẫn là một luồng đơn." Vì vậy, đó là không?
8protons

Tôi đề nghị bạn đọc phần còn lại của câu trả lời, đặc biệt là câu trả lời này và liên kết thứ hai tôi đã đăng.
Robert Harvey

2
@RobertHarvey Liên kết thứ hai bạn đã đăng, từ sự hiểu biết của tôi về những gì nó đang nói, đang nói về một trình biên dịch tạo ra một phiên bản đa luồng của ứng dụng được biên dịch của bạn. Đây không phải là về trình biên dịch. Cảm ơn bạn đã chia sẻ tài nguyên của bạn và dành thời gian để trả lời.
8protons

Câu trả lời:


29

Các dự án phần mềm lớn thường bao gồm nhiều đơn vị biên dịch có thể được biên dịch tương đối độc lập và do đó quá trình biên dịch thường được song song hóa ở mức độ chi tiết rất thô bằng cách gọi trình biên dịch nhiều lần song song. Điều này xảy ra ở cấp độ của các quy trình HĐH và được điều phối bởi hệ thống xây dựng chứ không phải trình biên dịch thích hợp. Tôi nhận ra đây không phải là những gì bạn yêu cầu nhưng đó là điều gần nhất để song song hóa trong hầu hết các trình biên dịch.

Tại sao vậy? Chà, phần lớn công việc mà các trình biên dịch không tự cho vay để song song hóa một cách dễ dàng:

  • Bạn không thể chia đầu vào thành nhiều phần và lex chúng một cách độc lập. Để đơn giản, bạn muốn phân tách trên các ranh giới lexme (để không có luồng nào bắt đầu ở giữa một lexme), nhưng việc xác định ranh giới lexme có khả năng đòi hỏi nhiều bối cảnh. Ví dụ: khi bạn nhảy vào giữa tệp, bạn phải đảm bảo rằng bạn đã không nhảy vào một chuỗi ký tự. Nhưng để kiểm tra điều này, bạn phải xem xét về cơ bản mọi nhân vật xuất hiện trước đây, công việc gần như chỉ đơn giản là bắt đầu với nó. Bên cạnh đó, lexing hiếm khi là nút cổ chai trong trình biên dịch cho các ngôn ngữ hiện đại.
  • Phân tích cú pháp thậm chí còn khó hơn để song song. Tất cả các vấn đề về phân tách văn bản đầu vào để từ chối thậm chí còn áp dụng nhiều hơn cho việc phân tách các mã thông báo để phân tích cú pháp --- ví dụ: xác định nơi một chức năng bắt đầu về cơ bản khó như phân tích nội dung chức năng để bắt đầu. Mặc dù cũng có thể có những cách xoay quanh vấn đề này, nhưng có lẽ chúng sẽ phức tạp một cách không cân xứng vì lợi ích nhỏ. Phân tích cú pháp cũng không phải là nút cổ chai lớn nhất.
  • Sau khi bạn phân tích cú pháp, bạn thường cần thực hiện phân giải tên, nhưng điều này dẫn đến một mạng lưới các mối quan hệ đan xen rất lớn. Để giải quyết một cuộc gọi phương thức ở đây, trước tiên bạn có thể phải giải quyết các lần nhập trong mô-đun này, nhưng những yêu cầu này phải giải quyết các tên trong một đơn vị biên dịch khác , v.v. Tương tự đối với suy luận kiểu nếu ngôn ngữ của bạn có điều đó.

Sau này, nó trở nên dễ dàng hơn một chút. Về nguyên tắc, việc kiểm tra và tối ưu hóa và tạo mã có thể được song song hóa ở mức độ chi tiết của hàm. Tôi vẫn biết rất ít nếu có bất kỳ trình biên dịch nào làm việc này, có lẽ bởi vì thực hiện bất kỳ nhiệm vụ nào đồng thời lớn này là khá khó khăn. Bạn cũng phải xem xét rằng hầu hết các dự án phần mềm lớn chứa rất nhiều đơn vị biên dịch nên cách tiếp cận "chạy một loạt trình biên dịch" là hoàn toàn đủ để giữ tất cả các lõi của bạn (và trong một số trường hợp, thậm chí là toàn bộ cụm máy chủ). Thêm vào đó, trong các tác vụ biên dịch lớn, I / O của đĩa có thể bị tắc nghẽn nhiều như công việc biên dịch thực tế.

Tất cả những gì đã nói, tôi biết một trình biên dịch song song với công việc tạo và tối ưu hóa mã. Trình biên dịch Rust có thể phân chia công việc phía sau (LLVM, thực sự bao gồm tối ưu hóa mã được coi là "trung cấp" theo truyền thống) giữa một số luồng. Điều này được gọi là "đơn vị mã gen". Ngược lại với các khả năng song song hóa khác được thảo luận ở trên, điều này là kinh tế vì:

  1. Ngôn ngữ có các đơn vị biên dịch khá lớn (so với C, Java), do đó, có thể có ít đơn vị biên dịch trong chuyến bay hơn so với lõi của bạn.
  2. Phần đang được song song hóa thường chiếm phần lớn thời gian biên dịch.
  3. Phần lớn công việc phụ trợ là phần song song, đáng xấu hổ - chỉ cần tối ưu hóa và dịch sang mã máy từng chức năng một cách độc lập. Tất nhiên, có các tối ưu hóa theo thủ tục và các đơn vị codegen cản trở chúng và do đó ảnh hưởng đến hiệu suất, nhưng không có bất kỳ vấn đề ngữ nghĩa nào.

2

Biên dịch là một vấn đề "song song xấu hổ".

Không ai quan tâm đến thời gian biên dịch một tập tin. Mọi người quan tâm đến thời gian biên dịch 1000 tập tin. Và đối với 1000 tệp, mỗi lõi của bộ xử lý có thể vui vẻ biên dịch một tệp cùng một lúc, giữ cho tất cả các lõi hoàn toàn bận rộn.

Mẹo: "make" sử dụng nhiều lõi nếu bạn cung cấp cho nó tùy chọn dòng lệnh phù hợp. Nếu không có nó, nó sẽ biên dịch một tệp khác trên hệ thống 16 lõi. Điều đó có nghĩa là bạn có thể làm cho nó biên dịch nhanh hơn 16 lần với một thay đổi một dòng cho các tùy chọn xây dựng của bạn.

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.