Hàm nội tuyến trong C ++. Vấn đề ở đây là gì?


19

Theo những gì tôi đọc, trình biên dịch không bắt buộc phải thay thế lời gọi hàm của hàm nội tuyến bằng phần thân của nó, nhưng sẽ làm như vậy nếu có thể. Điều này khiến tôi suy nghĩ - tại sao chúng ta có từ nội tuyến nếu đó là trường hợp? Tại sao không thực hiện tất cả các hàm nội tuyến theo mặc định và để trình biên dịch tìm hiểu xem nó có thể thay thế các cuộc gọi bằng thân hàm hay không?


1
Đây . Trang 6
EpsilonVector

Câu trả lời:


40

inlinelà từ C; nó không phải là mới đối với C ++.

Có những từ khóa C ( registerinline) được thiết kế để cho phép lập trình viên hỗ trợ tối ưu hóa mã. Ngày nay chúng thường bị bỏ qua, vì trình biên dịch có thể làm tốt hơn trong việc gán đăng ký và quyết định khi nào các hàm nội tuyến (trên thực tế, trình biên dịch có thể nội tuyến hoặc không nội tuyến một hàm tại các thời điểm khác nhau). Việc tạo mã trên các bộ xử lý hiện đại phức tạp hơn nhiều so với các mã xác định phổ biến hơn khi Ritchie đang phát minh ra C.

Từ này có nghĩa là gì trong C ++, là nó có thể có nhiều định nghĩa giống hệt nhau và cần được định nghĩa trong mọi đơn vị dịch sử dụng nó. (Nói cách khác, bạn cần đảm bảo rằng nó có thể được nội tuyến.) Bạn có thể có một inlinehàm trong tiêu đề không có vấn đề và các hàm thành viên được xác định trong định nghĩa lớp được tự động hiệu quả inline.


3
+1 cho lịch sử của inline.
Tamara Wijsman

11
Tôi khá chắc chắn rằng nó inlineđã được chuẩn hóa trong C ++ trước, mặc dù nó đã có sẵn như là một phần mở rộng của nhà cung cấp trong C .... yep, có vẻ như nó đã được thêm vào tiêu chuẩn C trong C99.
Ben Voigt

@Ben Voigt: Bạn nói đúng. Lần đầu tiên tôi bắt gặp nó ở C.
David Thornley

5
Cũng cần lưu ý rằng không chỉ lịch sử sai, mà các quy tắc inlinetrong C99 trở về sau cũng khác với các quy tắc inlinetrong C ++.
Alf P. Steinbach

27

Ban đầu inlinelà một gợi ý rất mạnh mẽ rằng các cuộc gọi đến chức năng nên được nội tuyến.

Nhưng hiệu quả được bảo đảm duy nhất inlinelà cho phép một chức năng được xác định (có hiệu quả giống hệt nhau) trong nhiều đơn vị dịch thuật, ví dụ: bạn đặt định nghĩa trong tệp tiêu đề.

Ngày nay, một số trình biên dịch rất quan tâm đến việc làm theo gợi ý nội tuyến, ví dụ g ++. Và một số trình biên dịch coi nó ít nghiêm trọng hơn, ví dụ Visual C ++. Nhưng tất cả phải tuân theo sự đảm bảo.

Thật không may là hai ý nghĩa này - gợi ý tối ưu hóa và cái mà chúng ta có thể gọi là một định nghĩa loại bỏ mức liên kết - nằm trong cùng một từ khóa, bởi vì điều đó có nghĩa là bạn thực tế không thể có cái này mà không có cái kia.

Thật không may là inline(hoặc tốt hơn, một từ khóa riêng về định nghĩa loại bỏ) cannot được áp dụng cho dữ liệu .

Nhu cầu về dữ liệu loại bỏ mức liên kết đã tăng lên khi các mô-đun chỉ tiêu đề đã trở nên phổ biến hơn. Ví dụ, nhiều thư viện phụ Boost chỉ dành cho tiêu đề.

Đối với dữ liệu bạn có thể, tuy nhiên, áp dụng một mẹo nhỏ với các mẫu. Xác định nó trong một số mẫu lớp, cung cấp một typedeftham số mẫu void(hoặc bất cứ điều gì). Đó là bởi vì Quy tắc Một Định nghĩa tạo một ngoại lệ cụ thể cho các mẫu.

Ghi chú:
¹ inlinebiến sẽ được hỗ trợ trong C ++ 17 .


1
+1 để biết thêm thông tin về inline.
Tamara Wijsman

1
" thực sự giống hệt nhau " thực sự là một điều kiện rất khó khăn, vì các ngôn ngữ C ++ được thiết kế rất tệ cố gắng hết sức để khiến các lập trình viên cẩn thận và có năng lực viết mã hợp lý nhưng chính thức không xác định sử dụng các đối tượng tĩnh - tôi tự hỏi có bao nhiêu thành viên ủy ban rơi vào bẫy
tò mò

6

Tại sao không làm cho tất cả các chức năng nội tuyến theo mặc định? Bởi vì đó là một sự đánh đổi kỹ thuật. Có ít nhất hai loại "tối ưu hóa": tăng tốc chương trình và giảm kích thước (dấu chân bộ nhớ) của chương trình. Nội tuyến thường tăng tốc mọi thứ. Nó được loại bỏ chức năng gọi qua chức năng, tránh đẩy sau đó kéo các tham số từ ngăn xếp. Tuy nhiên, nó cũng làm cho dung lượng bộ nhớ của chương trình lớn hơn, bởi vì mọi lệnh gọi hàm bây giờ phải được thay thế bằng mã đầy đủ của hàm. Để làm cho mọi thứ trở nên phức tạp hơn, hãy nhớ rằng CPU lưu trữ các khối bộ nhớ được sử dụng thường xuyên trong bộ đệm trên CPU để truy cập cực nhanh. Nếu bạn làm cho hình ảnh bộ nhớ của chương trình đủ lớn, chương trình của bạn sẽ không thể sử dụng bộ đệm một cách hiệu quả và trong trường hợp xấu nhất, nội tuyến có thể thực sự làm chậm chương trình của bạn.


5
Nhưng trình biên dịch có thể (và hiện!) Tìm ra điều này tốt hơn nhiều so với lập trình viên nói chung. Vì vậy, đây không phải là một đối số hợp lệ.
Konrad Rudolph

" mọi cuộc gọi chức năng bây giờ phải được thay thế bằng mã đầy đủ của chức năng " Và chức năng nội tuyến không phải là chức năng trong đó trình tự cuộc gọi bị bỏ qua và nơi mã lắp ráp của chức năng được sao chép tại chỗ. Một hàm nội tuyến được biên dịch tại chỗ, bằng cách đưa nội tuyến mã trung gian mức cao. Điều đó cho phép trình biên dịch xử lý phần thân hàm một cách hiệu quả như một macro sạch (một macro sạch là một macro không có các quirks của các macro tiền xử lý C), với khả năng tối ưu hóa nhiều khả năng.
tò mò

3

Để hiểu được nội tuyến, bạn cần hiểu lịch sử và cuộc sống như thế nào 20 (và 30) năm trước.

Chúng tôi đã viết mã trên các máy tính có ít bộ nhớ, do đó trình biên dịch không thể xử lý tất cả mã tạo ra một chương trình trong một lần. Trình biên dịch cũng rất chậm, vì vậy bạn không muốn phải biên dịch lại mã không thay đổi - mất hơn 24 giờ (trên một máy tính có giá cao hơn một chiếc xe hơi hàng đầu) để biên dịch lại tất cả mã là bình thường đối với một vài dự án tôi làm việc trên.

Do đó, mỗi tệp mã được biên dịch riêng biệt thành một tệp đối tượng. Mỗi tệp đối tượng bắt đầu với danh sách tất cả các chức năng mà nó chứa, cùng với địa chỉ của Google. Một tệp đối tượng cũng có một danh sách tất cả các chức năng mà nó được gọi trong các tệp đối tượng khác cùng với vị trí của cuộc gọi.

Trước tiên, một trình liên kết sẽ đọc tất cả các tệp đối tượng và xây dựng một danh sách tất cả các chức năng mà chúng xuất ra, cùng với tệp mà chúng có trong đó và có địa chỉ. Sau đó, nó sẽ đọc lại tất cả các tệp đối tượng, xuất chúng thành tệp chương trình, trong khi cập nhật tất cả các lệnh gọi hàm bên ngoài của hồi giáo với địa chỉ của hàm.

Trình liên kết không thay đổi hoặc tối ưu hóa mã máy do trình biên dịch tạo ra bằng bất kỳ cách nào khác ngoài việc sửa các tham chiếu đến các lệnh gọi hàm bên ngoài. Trình liên kết là một phần của hệ điều hành và có trước hầu hết các trình biên dịch. Khi mọi người viết một trình biên dịch mới, họ cần nó để làm việc với các trình liên kết hiện tại và để có thể liên kết với các tệp đối tượng hiện tại, nếu không các cuộc gọi hệ thống không thể được thực hiện.

Trình biên dịch chỉ nhìn thấy mã trong tập tin .cv hoặc hoặc .cppv mà nó đang biên dịch cùng với tất cả các tệp tiêu đề đi kèm. Vì vậy, nó không thể thực hiện bất kỳ tối ưu hóa nào dựa trên mã trong các tập tin khác.

Từ khóa trong dòng trực tuyến, cho phép phần thân của hàm (phương thức) được xác định trong tệp tiêu đề, do đó cho phép trình biên dịch sử dụng mã của hàm trong khi biên dịch mã gọi nó. Ví dụ: bạn đã có một lớp tập hợp được định nghĩa trong tệp .cpp bao phấn, lớp này sẽ có một phương thức is is ismpmp, có chứa một dòng mã, sẽ có một sự tăng tốc lớn của chương trình kết quả nếu thay vì gọi một hàm , cuộc gọi chức năng đã được thay thế bằng một dòng này.

Từ khóa trực tuyến trên nền tảng trực tuyến được xem là một cách rẻ tiền và dễ dàng để cho phép đóng gói dữ liệu trong khi tránh chi phí của các cuộc gọi hàm, nếu không có nhiều lập trình viên sẽ chỉ truy cập vào các trường riêng của đối tượng. (Macros, một cách tồi tệ hơn nhiều khi đặt nội tuyến mã vào nơi phổ biến tại thời điểm đó.)

Những ngày này, các trình liên kết của MIT, rất nhiều tối ưu hóa mã và có xu hướng được viết bởi một số nhóm làm trình biên dịch. Trình biên dịch thường chỉ kiểm tra mã là chính xác và nén nén nó, để lại hầu hết các nhiệm vụ tạo mã máy cho trình liên kết.


2

Hãy xem tiêu chuẩn nói gì (tô sáng những phần quan trọng được in đậm):

2. Một khai báo hàm với một bộ xác định nội tuyến khai báo một hàm nội tuyến. Trình xác định nội tuyến chỉ ra cho việc triển khai rằng việc thay thế nội tuyến của thân hàm tại điểm gọi sẽ được ưu tiên hơn đối với cơ chế gọi hàm thông thường. Việc triển khai là không bắt buộc để thực hiện thay thế nội tuyến này tại điểm gọi; tuy nhiên, ngay cả khi thay thế nội tuyến này bị bỏ qua, các quy tắc khác cho các chức năng nội tuyến vẫn sẽ được tôn trọng.

- Tiêu chuẩn C ++, ISO / IEC 14882: 2003 , 7.1.2 Bộ chỉ định chức năng [dcl.fct.spec]

Vì vậy, nếu bạn muốn chắc chắn, bạn nên đọc tài liệu của trình biên dịch.

Nội tuyến mọi thứ là một ý tưởng tồi, vì nó có thể dẫn đến rất nhiều mã máy trùng lặp ...

Vì vậy, bạn sẽ phải biết:

Không có câu trả lời đơn giản: Bạn phải chơi với nó để xem cái gì là tốt nhất. Đừng không giải quyết cho câu trả lời đơn giản như, "sử dụng bao giờ inlinechức năng" hay "Luôn luôn sử dụng inlinechức năng" hoặc "Sử dụng inlinechức năng khi và chỉ khi chức năng là ít hơn N dòng mã." Các quy tắc một kích thước phù hợp với tất cả các quy tắc này có thể dễ dàng viết ra, nhưng chúng sẽ tạo ra kết quả dưới tối ưu.

- Câu hỏi thường gặp về C ++, Hàm nội tuyến , 9.3 Các inlinechức năng có cải thiện hiệu suất không?


1
Những gì bạn nói là đúng, nhưng không liên quan vì với tư cách là người lập trình, đó không phải là quyết định của bạn hay không. Bạn đặt từ khóa, bạn có thể hoặc không thể thực hiện các chức năng của mình. Bạn không đặt từ khóa, bạn vẫn có thể hoặc không thể đặt chúng vào trong. Thật vô nghĩa khi có bất kỳ quy tắc nào khi bạn đặt từ khóa, điều gì với trình biên dịch là người thực sự đưa ra quyết định.
Kate Gregory

Làm thế nào điều này không liên quan nếu nó nói chính xác như bạn? Không có câu trả lời đơn giản .
Tamara Wijsman

Nó không liên quan vì nó không trả lời câu hỏi OP. Anh ấy không hỏi "nội tuyến làm gì", anh ấy hỏi "vấn đề là gì". Và câu trả lời của bạn chỉ nhắc lại những gì chúng ta đã biết về nội tuyến.
Thưởng thức dralo

@Davor: "Điểm gì?" không tuân thủ Câu hỏi thường gặp, vì vậy tôi đang trả lời các câu hỏi được đặt ra trong câu hỏi. Tôi đang nói đến vấn đề inlinebằng cách nhắc lại kiến ​​thức vì OP dường như bỏ lỡ một phần vì vậy đó là lý do cốt lõi tại sao anh ta không đạt được điểm. Để có được một điểm anh ta cần phải hiểu về những gì bên dưới điểm đó ...
Tamara Wijsman

Tại sao địa ngục không tuân thủ Câu hỏi thường gặp? Tiêu chuẩn mô tả cú pháp và ngữ nghĩa của từ khóa nội tuyến, và câu hỏi OP là mục đích là gì, khi nào nên sử dụng nó, vấn đề gì cần giải quyết? Tôi không thấy làm thế nào mà không tuân thủ Câu hỏi thường gặp.
Thưởng thức dralo

1

Hãy để tôi cung cấp cho bạn một lý do tốt để sử dụng từ khóa nội tuyến.

Trên một hệ thống nhúng, chẳng hạn như một máy in vé hoặc hệ thống nhỏ hơn tương tự. Bộ xử lý rất hạn chế và một lệnh gọi hàm (chuẩn bị các tham số chức năng trên ngăn xếp, gọi, tìm nạp các tham số từ ngăn xếp và trả lời lại, v.v.) có thể mất vài ms để thực thi, bên cạnh chức năng.

Hãy nói rằng thời gian cuộc gọi là khoảng 60ms (chỉ để gọi chứ không phải chức năng thực tế) và bạn đã thực hiện 50 lần lặp (các cuộc gọi lặp hoặc lặp trong một cây).

Thời gian để di chuyển qua lại từ chức năng đó sẽ mất 60 * 50 = 3000 (3 giây).

Nếu bạn có bộ nhớ, bạn chắc chắn sẽ thực hiện nội tuyến để tiết kiệm 3 giây.

Vì vậy, nội tuyến được sử dụng cơ bản khi bạn cần tốc độ thực hiện. Trong một số dự án tôi đã tham gia, thời gian gọi dài hơn thời gian thực hiện, một tình huống kinh điển khi sử dụng nội tuyến.


Ơ, cái gì? Đặt nội tuyến trong mã của bạn không có nghĩa là trình biên dịch sẽ thực sự nội tuyến và không đặt nó ở đó cũng không có nghĩa là nó sẽ không. Đó chỉ là một gợi ý, phần lớn bị bỏ qua bởi các trình biên dịch ngày nay. Nếu trình biên dịch thấy nó hữu ích cho nội tuyến, thì nó sẽ, nếu không, nó sẽ không. Bạn không có quyền kiểm soát thực sự ở đó. OP đã biết điều này và đang hỏi, tôi xin trích dẫn: "Ý của bạn là gì?". Ai là địa ngục cho +1 về điều này? Đó là cả sai lệch (ngụ ý rằng từ khóa nội tuyến thực thi nội tuyến) và không liên quan đến câu hỏi.
Davor dralo

2
@Davor dralo Không cần phải thô lỗ. Tôi giải thích câu hỏi là TẠI SAO tôi nên sử dụng nội tuyến? Quan điểm của tôi là chỉ ra rằng bạn có thể lấy mã nhanh hơn với chi phí bộ nhớ. Mỗi trình biên dịch có thể xử lý từ khóa nội tuyến khác nhau, vì vậy bạn cần kiểm tra tài liệu mà tôi không đề cập. Vì bạn không thể luôn luôn có đủ khả năng tạo ra các dòng nội dung bộ nhớ bổ sung, nên "hướng dẫn" khi nào nên sử dụng nó. Tôi cũng không nói rằng nội tuyến được thi hành. Ai đó thấy câu trả lời này hữu ích cho anh ấy / cô ấy và đã bình chọn +1, xin vui lòng tôn trọng rằng người khác có thể có một cấp độ kinh nghiệm khác và tìm thấy một cái gì đó tầm thường hữu ích.
Max Kielland
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.