2013-02-14 68 views
6

Tôi hiểu rằng OpenMP trên thực tế chỉ là một tập hợp các macro được biên dịch thành pthreads. Có cách nào nhìn thấy mã pthread trước khi phần còn lại của biên dịch xảy ra? Tôi đang sử dụng GCC để biên dịch.Biên dịch openmp thành pthreads Mã C

Trả lời

8

Đầu tiên, OpenMP là không một tập hợp macro đơn giản. Nó có thể được nhìn thấy một sự chuyển đổi đơn giản thành mã giống như pthread, nhưng OpenMP yêu cầu nhiều hơn bao gồm cả hỗ trợ thời gian chạy.

Quay lại câu hỏi của bạn, ít nhất, trong GCC, bạn không thể nhìn thấy mã pthreaded vì thực hiện OpenMP của GCC được thực hiện trong trình biên dịch back-end (hoặc trung cấp). Chuyển đổi được thực hiện trong IR (mức trung gian đại diện). Vì vậy, từ quan điểm của các lập trình viên, nó không dễ dàng để xem mã thực sự được chuyển đổi như thế nào.

Tuy nhiên, có một số tham chiếu.

(1) Một kỹ sư của Intel đã cung cấp một tổng quan tuyệt vời của việc thực hiện của OpenMP trong Intel C/C++:

http://www.drdobbs.com/parallel/how-do-openmp-compilers-work-part-1/226300148

http://www.drdobbs.com/parallel/how-do-openmp-compilers-work-part-2/226300277

(2) Bạn có thể có một cái nhìn tại triển khai OpenMP của GCC:

https://github.com/mirrors/gcc/tree/master/libgomp

Xem libgomp.h không sử dụng pthread và loop.c chứa việc triển khai cấu trúc vòng lặp song song.

-1

Tôi chưa thử nghiệm với openmp. Nhưng tùy chọn trình biên dịch -E sẽ cung cấp cho bạn mã sau khi xử lý trước.

+0

Không. 'gcc -E' không xử lý trước, nhưng không giải thích' # pragma', mà đến muộn hơn trong chuỗi biên dịch. –

5

OpenMP là tập hợp trình biên dịch chỉ thị, không phải macro. Trong C/C++, các chỉ thị này được triển khai với cơ chế mở rộng #pragma trong khi ở Fortran chúng được triển khai dưới dạng nhận xét được định dạng đặc biệt. Các chỉ thị này hướng dẫn trình biên dịch thực hiện một số biến đổi mã nhất định để chuyển đổi mã nối tiếp thành song song.

Mặc dù có thể triển khai OpenMP dưới dạng chuyển đổi sang mã pthreads thuần túy, điều này hiếm khi được thực hiện. Phần lớn các cơ chế OpenMP thường được xây dựng thành một thư viện thời gian chạy riêng biệt, vốn là một phần của bộ biên dịch. Đối với GCC, đây là libgomp. Nó cung cấp một tập hợp các hàm mức cao được sử dụng để dễ dàng triển khai các cấu trúc OpenMP. Nó cũng là nội bộ của trình biên dịch và không có ý định được sử dụng bởi mã người dùng, tức là không có tệp tiêu đề nào được cung cấp.

Với GCC, có thể có được biểu diễn giả mã của mã trông như thế nào sau khi chuyển đổi OpenMP. Bạn phải cung cấp tùy chọn -fdump-tree-all, điều này sẽ dẫn đến trình biên dịch sẽ tăng một lượng lớn tệp trung gian cho mỗi đơn vị biên dịch. Điều thú vị nhất là filename.017t.ompexp (điều này xuất phát từ GCC 4.7.1, con số có thể khác với các phiên bản GCC khác, nhưng phần mở rộng sẽ vẫn là .ompexp). Tệp này chứa một biểu diễn trung gian của mã sau khi các cấu trúc OpenMP được hạ xuống và sau đó được mở rộng vào triển khai thích hợp của chúng.

Xét đoạn mã sau ví dụ C, lưu lại dưới dạng fun.c:

void fun(double *data, int n) 
{ 
    #pragma omp parallel for 
    for (int i = 0; i < n; i++) 
    data[i] += data[i]*data[i]; 
} 

Nội dung của fun.c.017t.ompexp là:

fun (double * data, int n) 
{ 
    ... 
    struct .omp_data_s.0 .omp_data_o.1; 
    ... 

<bb 2>: 
    .omp_data_o.1.data = data; 
    .omp_data_o.1.n = n; 
    __builtin_GOMP_parallel_start (fun._omp_fn.0, &.omp_data_o.1, 0); 
    fun._omp_fn.0 (&.omp_data_o.1); 
    __builtin_GOMP_parallel_end(); 
    data = .omp_data_o.1.data; 
    n = .omp_data_o.1.n; 
    return; 
} 

fun._omp_fn.0 (struct .omp_data_s.0 * .omp_data_i) 
{ 
    int n [value-expr: .omp_data_i->n]; 
    double * data [value-expr: .omp_data_i->data]; 
    ... 

<bb 3>: 
    i = 0; 
    D.1637 = .omp_data_i->n; 
    D.1638 = __builtin_omp_get_num_threads(); 
    D.1639 = __builtin_omp_get_thread_num(); 
    ... 

<bb 4>: 
    ... this is the body of the loop ... 
    i = i + 1; 
    if (i < D.1644) 
    goto <bb 4>; 
    else 
    goto <bb 5>; 

<bb 5>: 

<bb 6>: 
    return; 

    ... 
} 

tôi đã bỏ qua phần lớn các đầu ra cho ngắn gọn. Đây không phải là mã C chính xác. Nó là biểu diễn dạng C của luồng chương trình. <bb N> là các khối cơ bản được gọi là - tập hợp các câu lệnh, được coi là các khối duy nhất trong luồng công việc của chương trình. Thứ đầu tiên mà người ta thấy là vùng song song được tách ra thành một hàm riêng biệt. Điều này không phổ biến - hầu hết các triển khai OpenMP làm nhiều hay ít việc chuyển đổi mã giống nhau. Người ta cũng có thể quan sát thấy trình biên dịch chèn các cuộc gọi đến các hàm libgomp như GOMP_parallel_startGOMP_parallel_end, được sử dụng để khởi động và sau đó hoàn thành việc thực hiện một vùng song song (tiền tố __builtin_ được xóa sau này). Bên trong fun._omp_fn.0 có một vòng lặp for, được triển khai trong <bb 4> (lưu ý rằng chính vòng lặp cũng được mở rộng). Ngoài ra tất cả các biến chia sẻ được đưa vào một cấu trúc đặc biệt được truyền cho việc thực hiện vùng song song. <bb 3> chứa mã tính toán phạm vi các lần lặp mà chuỗi hiện tại sẽ hoạt động.

Vâng, không hoàn toàn là mã C, nhưng đây có lẽ là điều gần nhất mà người ta có thể nhận được từ GCC.

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