Tại sao không biên dịch nội tuyến mọi thứ? [đóng cửa]


12

Đôi khi biên dịch các hàm gọi nội tuyến. Điều đó có nghĩa là họ di chuyển mã của hàm được gọi vào hàm gọi. Điều này làm cho mọi thứ nhanh hơn một chút vì không cần phải đẩy và bật các thứ trên và ngoài ngăn xếp cuộc gọi.

Vì vậy, câu hỏi của tôi là, tại sao không biên dịch nội tuyến tất cả mọi thứ? Tôi cho rằng nó sẽ làm cho việc thực thi nhanh hơn đáng kể.

Lý do duy nhất tôi có thể nghĩ đến là một tệp thực thi lớn hơn đáng kể, nhưng nó có thực sự quan trọng trong những ngày này với hàng trăm GB bộ nhớ không? Không phải hiệu suất được cải thiện có đáng không?

Có bất kỳ lý do nào khác tại sao trình biên dịch không chỉ nội tuyến tất cả các lệnh gọi hàm không?


17
IDK về bạn, nhưng tôi không có hàng trăm GB bộ nhớ chỉ nằm xung quanh.
Ampt

2
Isn't the improved performance worth it?Đối với một phương thức sẽ chạy một vòng lặp 100 lần và xử lý một số con số nghiêm trọng, thì việc chuyển 2 hoặc 3 đối số sang các thanh ghi CPU là không có gì.
Doval

5
Bạn quá chung chung, "trình biên dịch" có nghĩa là "tất cả các trình biên dịch" và "mọi thứ" có nghĩa thực sự là "mọi thứ" không? Sau đó, câu trả lời là đơn giản, có những tình huống mà bạn chỉ đơn giản là không thể nội tuyến. Đệ quy đến với tâm trí.
Otávio Décio

17
Bộ nhớ cache cục bộ là một cách quan trọng hơn nhiều so với chi phí gọi hàm nhỏ.
SK-logic

3
Liệu cải thiện hiệu suất có thực sự quan trọng trong những ngày này với hàng trăm GFLOPS của sức mạnh cung cấp?
mouviciel

Câu trả lời:


22

Đầu tiên lưu ý rằng một tác dụng chính của nội tuyến là nó cho phép tối ưu hóa hơn nữa được thực hiện tại trang web cuộc gọi.

Đối với câu hỏi của bạn: có những điều khó hoặc thậm chí không thể nội tuyến:

  • thư viện liên kết động

  • chức năng xác định động (công văn động, được gọi thông qua con trỏ chức năng)

  • chức năng đệ quy (có thể đệ quy đuôi)

  • các hàm mà bạn không có mã (nhưng tối ưu hóa thời gian liên kết cho phép điều này đối với một số trong số chúng)

Sau đó, nội tuyến không chỉ có tác dụng có lợi:

  • thực thi lớn hơn có nghĩa là vị trí đĩa nhiều hơn và thời gian tải lớn hơn

  • thực thi lớn hơn có nghĩa là tăng áp lực bộ đệm (lưu ý rằng nội tuyến các hàm đủ nhỏ như getters đơn giản có thể làm giảm kích thước thực thi và áp lực bộ đệm)

Và cuối cùng, đối với các hàm mất một thời gian không quan trọng để thực thi, mức tăng chỉ không đáng để chịu đựng.


3
một số cuộc gọi đệ quy có thể được nội tuyến (cuộc gọi đuôi), nhưng tất cả có thể được chuyển đổi thành lặp nếu bạn tùy ý thêm một ngăn xếp rõ ràng
ratchet freak

@ratchetfreak, bạn cũng có thể chuyển đổi một số lệnh gọi đệ quy không đuôi thành đuôi. Nhưng đó là đối với tôi trong thế giới "khó khăn" (đặc biệt là khi bạn có chức năng đệ quy hoặc phải xác định linh hoạt nơi để nhảy để mô phỏng lợi nhuận), nhưng điều đó là không thể (bạn chỉ cần đặt một khung tiếp tục và xem xét rằng hiện tại nó trở nên dễ dàng hơn).
Lập trình viên

11

Một hạn chế chính là đa hình thời gian chạy. Nếu có một công văn động xảy ra khi bạn viết foo.bar()thì không thể thực hiện cuộc gọi phương thức. Điều này giải thích tại sao trình biên dịch không nội tuyến mọi thứ.

Các cuộc gọi đệ quy cũng không thể dễ dàng được nội tuyến.

Nội tuyến chéo cũng khó thực hiện vì lý do kỹ thuật (không thể biên dịch lại gia tăng)

Tuy nhiên, trình biên dịch làm nội tuyến rất nhiều thứ.


3
Nội tuyến thông qua một công văn ảo là rất khó, nhưng không phải là không thể. Một số trình biên dịch C ++ có thể làm điều đó trong những trường hợp nhất định.
bstamour

2
... cũng như một số trình biên dịch JIT (devirtualization).
Frank

@bstamour Bất kỳ trình biên dịch nửa vời nào của bất kỳ ngôn ngữ nào có tối ưu hóa phù hợp sẽ gửi tĩnh, tức là devirtualise, một cuộc gọi đến một phương thức ảo được khai báo trên một đối tượng có kiểu động có thể biết được trong thời gian biên dịch. Điều này có thể tạo điều kiện cho nội tuyến nếu pha lệch pha xảy ra trước pha nội tuyến (hoặc khác). Nhưng điều này là tầm thường. Có ý gì khác bạn muốn nói? Tôi không thấy bất kỳ "Nội tuyến thông qua một công văn ảo" nào có thể đạt được. Để nội tuyến, người ta phải biết loại tĩnh - tức là devirtualise - vì vậy sự tồn tại của nội tuyến có nghĩa không có công văn ảo
underscore_d

9

Đầu tiên, bạn không thể luôn luôn nội tuyến, ví dụ các hàm đệ quy có thể không phải lúc nào cũng có thể inlinable (nhưng một chương trình chứa định nghĩa đệ quy factchỉ với một bản in fact(8)có thể được nội tuyến).

Sau đó, nội tuyến không phải lúc nào cũng có lợi. Nếu trình biên dịch nội tuyến nhiều đến mức mã kết quả đủ lớn để các phần nóng của nó không khớp với bộ đệm của lệnh L1, thì nó có thể chậm hơn nhiều so với phiên bản không nội tuyến (có thể dễ dàng phù hợp với bộ đệm L1) ... Ngoài ra, các bộ xử lý gần đây rất nhanh trong việc thực hiện một CALLlệnh máy (ít nhất là đến một vị trí đã biết, tức là một cuộc gọi trực tiếp, không phải là một con trỏ thông qua cuộc gọi).

Cuối cùng, nội tuyến đầy đủ đòi hỏi một phân tích toàn bộ chương trình. Điều này có thể không thể (hoặc quá tốn kém). Với C hoặc C ++ được biên dịch bởi GCC (và cả với Clang / LLVM ), bạn cần kích hoạt tối ưu hóa thời gian liên kết (bằng cách biên dịch và liên kết với ví dụ g++ -flto -O2) và điều đó mất khá nhiều thời gian biên dịch.


1
Đối với bản ghi, LLVM / Clang (và một số trình biên dịch khác) cũng hỗ trợ tối ưu hóa thời gian liên kết .
Bạn

Tôi biết điều đó; LTO tồn tại trong thế kỷ trước (ít nhất là IIRC, trong một số trình biên dịch độc quyền MIPS).
Basile Starynkevitch

7

Đáng ngạc nhiên là có vẻ như, nội tuyến mọi thứ không nhất thiết phải giảm thời gian thực hiện. Kích thước mã tăng lên của bạn có thể khiến CPU gặp khó khăn trong việc giữ tất cả mã của bạn trong bộ đệm của nó cùng một lúc. Một lỗi bộ nhớ cache trên mã của bạn trở nên có khả năng hơn và một lỗi bộ nhớ cache đắt tiền. Điều này được thực hiện tồi tệ hơn nhiều nếu các chức năng có khả năng nội tuyến của bạn lớn.

Thỉnh thoảng tôi đã cải thiện hiệu suất đáng chú ý bằng cách lấy các đoạn mã lớn được đánh dấu là 'nội tuyến' ra khỏi các tệp tiêu đề, đặt chúng vào mã nguồn, do đó mã chỉ ở một nơi thay vì ở mọi trang web cuộc gọi. Sau đó, bộ đệm CPU được sử dụng tốt hơn và bạn cũng có được thời gian biên dịch tốt hơn ...


điều này dường như chỉ lặp lại các điểm được thực hiện và giải thích trong câu trả lời trước đó đã được đăng một giờ trước
gnat

1
Những gì lưu trữ? L1? L2? L3? Cái nào quan trọng hơn?
Peter Mortensen

1

Nội tuyến mọi thứ không có nghĩa là chỉ tăng mức tiêu thụ bộ nhớ đĩa mà còn tăng mức tiêu thụ bộ nhớ trong mà không dồi dào. Hãy nhớ rằng mã cũng dựa vào bộ nhớ trong đoạn mã; nếu một hàm được gọi từ 10000 vị trí (giả sử các hàm từ các thư viện chuẩn trong một dự án khá lớn), thì mã cho hàm đó chiếm bộ nhớ trong gấp 10000 lần.

Một lý do khác có thể là trình biên dịch JIT; nếu mọi thứ là nội tuyến thì sẽ không có điểm nóng nào được biên dịch động.


1

Một, có những ví dụ đơn giản trong đó việc sắp xếp mọi thứ sẽ diễn ra rất tệ. Hãy xem xét mã C đơn giản này:

void f1 (void) { printf ("Hello, world\n"); }
void f2 (void) { f1 (); f1 (); f1 (); f1 (); }
void f3 (void) { f2 (); f2 (); f2 (); f2 (); }
...
void f99 (void) { f98 (); f98 (); f98 (); f98 (); }

Đoán những gì nội tuyến sẽ làm cho bạn.

Tiếp theo, bạn đưa ra giả định rằng nội tuyến sẽ làm mọi thứ nhanh hơn. Đó là trường hợp đôi khi, nhưng không phải luôn luôn. Một lý do là mã phù hợp với bộ đệm hướng dẫn chạy nhanh hơn rất nhiều. Nếu tôi gọi một hàm từ 10 vị trí, tôi sẽ luôn chạy mã trong bộ đệm hướng dẫn. Nếu nó được nội tuyến, thì các bản sao ở khắp mọi nơi và chạy chậm hơn rất nhiều.

Có những vấn đề khác: Nội tuyến tạo ra các chức năng rất lớn. Các chức năng khổng lồ khó hơn rất nhiều để tối ưu hóa. Tôi đã đạt được mức tăng đáng kể trong mã quan trọng về hiệu năng bằng cách ẩn các hàm vào một tệp riêng biệt để ngăn trình biên dịch nội tuyến. Do đó, mã được tạo cho các hàm này tốt hơn nhiều khi chúng bị ẩn.

BTW. Tôi không có "hàng trăm GB bộ nhớ". Máy tính hoạt động của tôi thậm chí không có "hàng trăm GB dung lượng ổ cứng". Và nếu ứng dụng của tôi có "bộ nhớ hàng trăm GB", sẽ mất 20 phút để tải ứng dụng vào bộ nhớ.

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.