Làm thế nào tôi có thể nói với gcc không nội tuyến một chức năng?


126

Nói rằng tôi có chức năng nhỏ này trong một tệp nguồn

static void foo() {}

và tôi xây dựng một phiên bản tối ưu hóa nhị phân của mình nhưng tôi không muốn chức năng này được nội tuyến (cho mục đích tối ưu hóa). Có một macro tôi có thể thêm vào một mã nguồn để ngăn chặn nội tuyến không?


Cảm ơn câu hỏi này! Tôi đã định hình với oprofile khi một chức năng không hiển thị, các câu trả lời ở đây đã khắc phục điều này.
Simon A. Eugster

Câu trả lời:


149

Bạn muốn thuộc tính gcc-specific noinline.

Thuộc tính chức năng này ngăn chặn một chức năng được xem xét để nội tuyến. Nếu chức năng không có tác dụng phụ, sẽ có các tối ưu hóa khác với nội tuyến khiến các lệnh gọi hàm được tối ưu hóa, mặc dù lệnh gọi hàm đang hoạt động. Để giữ cho các cuộc gọi như vậy được tối ưu hóa đi, hãy đặt asm ("");

Sử dụng nó như thế này:

void __attribute__ ((noinline)) foo() 
{
  ...
}

32
Sử dụng gcc 4.4.3 trên Arch Linux, tôi gặp lỗi cú pháp với thuộc tính được đặt như trên. Nó hoạt động chính xác khi nó đứng trước hàm (ví dụ: thuộc tính ((noinline)) void foo () {})
mrkj

2
Arduino cũng muốn nó được đặt trước chức năng.
Peter N Lewis

2
Chỉnh sửa để sửa cú pháp thuộc tính.
Quuxplusone

1
Cấu trúc asm ("") thực sự khá đa nền tảng và hoàn thành công việc. Tôi đã làm điều đó cho x86 Linux và nó không gây ra sự cố xây dựng trên PowerPC AIX. Cảm ơn lời đề nghị hữu ích này!
Marty

1
Cách tiếp cận yêu cầu thay đổi mã ở mọi nơi có thể được coi là một câu trả lời hợp lý.
ajeh

31

GCC có một công tắc được gọi là

-fno-inline-small-functions

Vì vậy, sử dụng khi gọi gcc. Nhưng tác dụng phụ là tất cả các chức năng nhỏ khác cũng không được nội tuyến.


Không làm việc ở cấp độ trình biên dịch. Đã sử dụng gcc 5.2.1 20150902 (Mũ đỏ 5.2.1-2)
John Greene

GCC 6.4 hiện tại đã bị hỏng hoặc điều này và đơn giản hơn hoàn -fno-inlinetoàn không hoạt động.gdbvẫn nhập các phương thức trên bước. Một cái gì đó bị phá vỡ, và tôi nghi ngờ nó là gdb.
ajeh

Nó sẽ tắt tối ưu hóa nội tuyến cho tất cả, không chỉ cho một chức năng được chỉ định.
where23

@ajeh Không phải hàm nội tuyến có nghĩa là chúng được gọi bình thường, phải không?
Melebius

21

Một cách di động để làm điều này là gọi hàm thông qua một con trỏ:

void (*foo_ptr)() = foo;
foo_ptr();

Mặc dù điều này tạo ra các hướng dẫn khác nhau để phân nhánh, có thể không phải là mục tiêu của bạn. Mà sẽ trả về một điểm tốt: những gì mục tiêu của bạn ở đây?


2
Nếu con trỏ được xác định ở phạm vi tệp và không tĩnh, thì nó sẽ hoạt động vì trình biên dịch không thể cho rằng nó có giá trị ban đầu tại thời điểm sử dụng. Nếu đó là một địa phương (như được hiển thị), nó gần như chắc chắn được đối xử giống như foo (). ("Trong thập kỷ này", ông nói thêm, nhìn vào các ngày)
greggo

16

Tôi biết câu hỏi là về GCC, nhưng tôi nghĩ nó có thể hữu ích khi có một số thông tin về trình biên dịch khác.

noinline Thuộc tính hàm của GCC cũng khá phổ biến với các trình biên dịch khác. Nó được hỗ trợ bởi ít nhất:

  • Clang (kiểm tra với __has_attribute(noinline))
  • Trình biên dịch Intel C / C ++ (tài liệu của họ rất tệ, nhưng tôi chắc chắn rằng nó hoạt động trên 16.0+)
  • Oracle Solaris Studio trở lại ít nhất 12.2
  • Trình biên dịch ARM C / C ++ trở lại ít nhất 4.1
  • IBM XL C / C ++ trở lại ít nhất 10.1
  • TI 8.0+ (hoặc 7.3+ với --gcc, sẽ xác định __TI_GNU_ATTRIBUTE_SUPPORT__)

Ngoài ra, MSVC hỗ trợ __declspec(noinline) quay lại Visual Studio 7.1. Intel cũng có thể hỗ trợ nó (họ cố gắng tương thích với cả GCC và MSVC), nhưng tôi không bận tâm để xác minh điều đó. Cú pháp về cơ bản là giống nhau:

__declspec(noinline)
static void foo(void) { }

PGI 10.2+ (và có thể cũ hơn) hỗ trợ một noinlinepragma áp dụng cho chức năng tiếp theo:

#pragma noinline
static void foo(void) { }

TI 6.0+ hỗ trợ một FUNC_CANNOT_INLINE pragma mà (thật khó chịu) hoạt động khác nhau trong C và C ++. Trong C ++, nó tương tự như PGI:

#pragma FUNC_CANNOT_INLINE;
static void foo(void) { }

Trong C, tuy nhiên, tên hàm được yêu cầu:

#pragma FUNC_CANNOT_INLINE(foo);
static void foo(void) { }

Cray 6.4+ (và có thể sớm hơn) có cách tiếp cận tương tự, yêu cầu tên hàm:

#pragma _CRI inline_never foo
static void foo(void) { }

Oracle Developer Studio cũng hỗ trợ một pragma lấy tên hàm, quay trở lại ít nhất là Forte Developer 6 , nhưng lưu ý rằng nó cần phải xuất hiện sau khai báo, ngay cả trong các phiên bản gần đây:

static void foo(void);
#pragma no_inline(foo)

Tùy thuộc vào mức độ tận tâm của bạn, bạn có thể tạo một macro hoạt động ở mọi nơi, nhưng bạn sẽ cần phải có tên hàm cũng như khai báo làm đối số.

Nếu, OTOH, bạn ổn với thứ gì đó chỉ phù hợp với hầu hết mọi người, bạn có thể thoát khỏi thứ gì đó có tính thẩm mỹ hơn một chút và không cần phải lặp lại. Đó là cách tiếp cận tôi đã thực hiện cho Hedley , nơi phiên bản hiện tại của HEDLEY_NEVER_INLINE trông giống như:

#if \
  HEDLEY_GNUC_HAS_ATTRIBUTE(noinline,4,0,0) || \
  HEDLEY_INTEL_VERSION_CHECK(16,0,0) || \
  HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
  HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
  HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
  HEDLEY_TI_VERSION_CHECK(8,0,0) || \
  (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
#  define HEDLEY_NEVER_INLINE __attribute__((__noinline__))
#elif HEDLEY_MSVC_VERSION_CHECK(13,10,0)
#  define HEDLEY_NEVER_INLINE __declspec(noinline)
#elif HEDLEY_PGI_VERSION_CHECK(10,2,0)
#  define HEDLEY_NEVER_INLINE _Pragma("noinline")
#elif HEDLEY_TI_VERSION_CHECK(6,0,0)
#  define HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;")
#else
#  define HEDLEY_NEVER_INLINE HEDLEY_INLINE
#endif

Nếu bạn không muốn sử dụng Hedley (đó là một tên miền công cộng / tiêu đề Muff), bạn có thể chuyển đổi các macro kiểm tra phiên bản mà không cần quá nhiều nỗ lực, nhưng nhiều hơn tôi sẵn sàng đưa vào.


Cảm ơn các liên kết đến dự án của bạn @nemequ. Tôi đã yêu cầu các nhà phát triển khác của chúng tôi đánh giá nó để sử dụng. Chúng tôi có kiến ​​trúc đa dạng.
Daisuke Aramaki

Tôi rất muốn biết họ nói gì, đặc biệt nếu họ không quan tâm. Và, tất nhiên, tôi ở xung quanh để trả lời các câu hỏi (trình theo dõi vấn đề GitHub, e-mail, bất cứ điều gì về đường sắt).
nemequ

14

Trong trường hợp bạn gặp lỗi trình biên dịch __attribute__((noinline)), bạn chỉ cần thử:

noinline int func(int arg)
{
    ....
}

10
static __attribute__ ((noinline))  void foo()
{

}

Đây là những gì làm việc cho tôi.


8

Sử dụng noinline thuộc tính :

int func(int arg) __attribute__((noinline))
{
}

Bạn có thể nên sử dụng cả khi bạn khai báo hàm để sử dụng bên ngoài và khi bạn viết hàm.


2

Tôi làm việc với gcc 7.2. Tôi đặc biệt cần một chức năng để không nội tuyến, bởi vì nó phải được khởi tạo trong một thư viện. Tôi đã thử __attribute__((noinline))câu trả lời, cũng nhưasm("") câu trả lời. Không ai giải quyết được vấn đề.

Cuối cùng, tôi đã hình dung rằng việc xác định một biến tĩnh bên trong hàm sẽ buộc trình biên dịch phân bổ không gian cho nó trong khối biến tĩnh và đưa ra một khởi tạo cho nó khi hàm được gọi lần đầu tiên.

Đây là một loại mánh khóe bẩn thỉu, nhưng nó hoạt động.


Bạn có thể xác định chức năng của mình inline void foo(void) { ... }trong một tiêu đề và khai báo nó extern inline void foo(void);trong tệp nguồn thư viện. Theo ngữ nghĩa C99, trình biên dịch sẽ được phép nội tuyến hàm khi nó làm hài lòng VÀ phát ra mã đối tượng trong thư viện của bạn. Xem "nội tuyến" không có "tĩnh" hay "bên ngoài" có bao giờ hữu ích trong C99 không? .
diapir
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.