2010-05-05 44 views
5

Tôi đã suy nghĩ về một vấn đề điển hình đó là rất JIT-thể, nhưng khó tiếp cận với nguyên C. Kịch bản được thiết lập một loạt các con trỏ chức năng đang đi để được "sáng tác" (như trong thành phần chức năng toán học) một lần tại thời gian chạy và sau đó được gọi là rất nhiều và rất nhiều lần."Inlining" (loại) chức năng tại thời gian chạy trong C

Làm theo cách rõ ràng liên quan đến nhiều cuộc gọi ảo, tốn kém và nếu có đủ chức năng lồng nhau để lấp đầy bảng dự đoán nhánh CPU hoàn toàn, thì hiệu suất giảm đáng kể. Trong một ngôn ngữ như Lisp, tôi có thể xử lý mã và thay thế cuộc gọi "ảo" bằng nội dung thực tế của các hàm và sau đó gọi compile để có phiên bản được tối ưu hóa, nhưng dường như rất dễ bị lỗi và dễ xảy ra lỗi trong C, và sử dụng C là một yêu cầu cho vấn đề này ;-)

Vì vậy, bạn có biết nếu có một cách tiêu chuẩn, di động và an toàn để đạt được điều này trong C?

Chúc mừng

Trả lời

6

Bạn có thể muốn xem LLVM. Họ có một thư viện cho phép mã JITing (và những thứ nhiều hơn nữa) từ C, nó hỗ trợ nhiều nền tảng và nó là một dự án mã nguồn mở: http://llvm.org/

+1

Vâng, đó là một lựa chọn Tôi đã xem xét, nhưng tôi đã suy nghĩ về một cái gì đó đơn giản hơn, tôi không nghĩ rằng Tôi cần toàn bộ JIT, chỉ cần dán một số chức năng với nhau như thể chúng chỉ là một chức năng lớn. Thanks anyway :-) – fortran

1

Tôi có hai đề xuất về cách loại bỏ chức năng cuộc gọi ảo của bạn, nếu cần thiết cho hiệu suất. Vì mục đích minh họa, giả sử bạn có một chức năng tham gia một con trỏ hàm như một cuộc tranh cãi:

void my_algorithm(int (*func)(...), ...) 
{ 
    /* ... */ 
} 

Và cũng giả sử bạn biết trước tất cả các giá trị có thể con trỏ chức năng có thể thực hiện. Ví dụ:

my_algorithm(func_1, ...); 
//... 
my_algorithm(func_2, ...); 

Đầu chuyển đổi my_algorithm ban đầu của bạn() vào một macro:

#define MY_ALGORITHM(func, ...)  \ 
{          \ 
    /* ... */       \ 
} 

Sau đó viết lại my_algorithm() như:

extern int func_1(...); 
extern int func_2(...); 

void my_algorithm(int (*func)(...), ...) 
{ 
    if (func == func_1) 
     MY_ALGORITHM(func_1, ...) 
    else if (func == func_2) 
     MY_ALGORITHM(func_2, ...) 
    else 
     assert(0 && "Unexpected function arg to my_algorithm()"); 
} 

này tất nhiên sẽ tăng gấp đôi kích thước của tệp đối tượng được biên dịch. Và, trên bề mặt, nó loại bỏ chỉ có một mức độ bất định. Nhưng nếu func_1 và/hoặc func_2 được gạch chân, bạn có thể tăng tốc đáng kể.

Và bạn thậm chí có thể 'vượt qua' macro, như trong:

#define HYPOT_Y(x) hypot(x, y) 
MY_ALGORITHM(HYPOT_Y, ...);  //assumes y is known 

Đề nghị thứ hai là một biến thể của việc này, sử dụng X Macros (http://en.wikipedia.org/wiki/C_preprocessor#X-Macros). Thay vì #define, hãy đặt phần thân của my_algorithm gốc() vào một tệp riêng biệt, my_algorithm.h. Sau đó viết lại my_algorithm() dưới dạng:

void my_algorithm(int (*func)(...), ...) 
{ 
    if (func == func_1) 
     #define func func_1 
     #include "my_algorithm.h" 
     #undef func 
    else if (func == func_2) 
     #define func func_2 
     #include "my_algorithm.h" 
     #undef func 
    else 
     assert(0 && "Unexpected function arg to my_algorithm()"); 
} 

Tôi có thể sử dụng macro X nếu mã nhiều hơn một vài chục dòng. lợi thế của mình bao gồm (không ý định chơi chữ):

  • Không backslashes xấu xí
  • gỡ lỗi dễ dàng hơn (ví dụ dấu vết trở lại và đơn bước).

Đây là tất cả tiêu chuẩn C. Nhưng đúng là trường cũ.

+1

Nếu bạn làm điều này, xin vui lòngplease sử dụng một tuyên bố chuyển đổi và vượt qua trong một số nguyên/enum hơn là sử dụng một bó nếu/else nếu/etc. Công tắc SO nhanh hơn nhiều. – SoapBox

+0

Công tắc trong C chỉ hoạt động trên các loại tích phân. –

+0

Bạn có thể bật một hằng số thay vì con trỏ hàm. Đừng lấy địa chỉ hàm nếu bạn không cần nó, nó buộc trình biên dịch cung cấp một bản sao không có nội tuyến của hàm chỉ để cung cấp cho con trỏ của bạn. Ngoài ra, nội tuyến (với hầu hết các trình biên dịch C) sẽ không hoạt động với extern, vì các hàm phải được bao gồm trong cùng một đơn vị biên dịch. – wump

0

Nếu bạn ok với chỉ hỗ trợ x86, bạn có thể thử nhúng TCC:

http://bellard.org/tcc/

+0

thật đáng buồn là tôi cần mã để di chuyển sang các nền tảng khác nhau :-(cảm ơn liên kết mặc dù có thể thú vị khi xem xét! – fortran

+1

Hơn LLVM là nền tảng của bạn. Nó không phải là xấu, sau khi tất cả, và nó dễ sử dụng qua một API C đơn giản Một cách khác là triển khai mã chuỗi Forth intrreter trong C (sử dụng phần mở rộng GCC được tính toán) và tạo mã cho nó. Nên chậm hơn 2-6 lần so với JIT gốc, nhưng tầm thường và hoàn toàn Nó đủ tốt để chỉ dán các chức năng với nhau. –

Các vấn đề liên quan