2009-08-18 36 views
17

Tóm tắt: Tôi muốn tận dụng tối ưu hóa trình biên dịch và bộ hướng dẫn xử lý, nhưng vẫn có ứng dụng di động (chạy trên các bộ xử lý khác nhau). Thông thường tôi thực sự có thể biên dịch 5 lần và cho phép người dùng chọn đúng để chạy.Biên dịch và tối ưu hóa cho các kiến ​​trúc đích khác nhau

Câu hỏi của tôi là: làm thế nào tôi có thể tự động hóa điều này, để bộ vi xử lý được phát hiện trong thời gian chạy và thực thi đúng được thực thi mà không cần người dùng phải chọn nó?


Tôi có một ứng dụng có nhiều phép tính toán ở mức độ thấp. Những tính toán này thường sẽ chạy trong một thời gian dài.

Tôi muốn tận dụng tối ưu hóa càng nhiều càng tốt, tốt nhất là các tập lệnh (không phải luôn được hỗ trợ). Mặt khác, tôi muốn ứng dụng của tôi có thể di chuyển và dễ sử dụng (vì vậy tôi không muốn biên dịch 5 phiên bản khác nhau và cho phép người dùng chọn).

Có khả năng biên dịch 5 phiên bản khác nhau của mã của tôi và chạy tự động phiên bản được tối ưu hóa nhất có thể vào thời gian thực thi không? Với 5 phiên bản khác nhau, tôi có ý nghĩa với các bộ hướng dẫn khác nhau và tối ưu hóa khác nhau cho bộ vi xử lý.

Tôi không quan tâm đến kích thước của ứng dụng.

Tại thời điểm này tôi đang sử dụng gcc trên Linux (mã của tôi bằng C++), nhưng tôi cũng quan tâm đến điều này cho trình biên dịch Intel và trình biên dịch MinGW để biên dịch sang Windows.

Tệp thực thi không nhất thiết phải chạy trên các hệ điều hành khác nhau, nhưng lý tưởng là có thể có thể tự động chọn 32 bit và 64 bit.

Chỉnh sửa: Vui lòng cung cấp cho con trỏ rõ ràng cách thực hiện, tốt nhất là với các ví dụ mã nhỏ hoặc liên kết đến giải thích. Theo quan điểm của tôi, tôi cần một giải pháp siêu chung, được áp dụng cho bất kỳ dự án C++ ngẫu nhiên nào mà tôi có sau này.

Chỉnh sửa Tôi đã giao tiền thưởng cho ShuggyCoUk, anh ấy có nhiều con trỏ để tìm kiếm. Tôi đã có thể thích chia nó giữa nhiều câu trả lời nhưng điều đó là không thể. Tôi chưa thực hiện điều này, vì vậy câu hỏi vẫn là 'mở'! Xin vui lòng, vẫn thêm và/hoặc cải thiện câu trả lời, mặc dù không có tiền thưởng được đưa ra nữa.

Cảm ơn mọi người!

+0

Đây không phải là những gì Apple thực hiện với các tệp nhị phân "Phổ" (PPC - x86) của họ không? – Edmundo

+0

Tôi đảm bảo rằng tôi đã +1 tất cả các câu trả lời mà tôi nghĩ là tốt, tất cả họ đều nhận được một chút từ tôi :). Chúc mừng cho chấp nhận. – ShuggyCoUk

+0

Oh và nếu bạn tìm hiểu thêm thông tin khi bạn đi cùng hãy chỉnh sửa câu trả lời của tôi và làm cho nó CW ... – ShuggyCoUk

Trả lời

5

Nếu bạn muốn tính năng này hoạt động hiệu quả trên Windows và tận dụng tối đa nền tảng có khả năng 64 bit của địa chỉ bổ sung 1. Địa chỉ và 2. sổ đăng ký (có thể sử dụng nhiều hơn), bạn phải có tối thiểu một quy trình riêng biệt các 64bit.

Bạn có thể đạt được điều này bằng cách thực thi riêng biệt với tiêu đề PE64 có liên quan. Đơn giản chỉ cần sử dụng CreateProcess sẽ ra mắt này như bitness liên quan (trừ trường hợp thực thi đưa ra là ở một số vị trí chuyển hướng không cần phải lo lắng về WoW64 folder redirection

Với hạn chế này trên cửa sổ có khả năng là chỉ đơn giản là 'chaining cùng' để thực thi có liên quan sẽ là tùy chọn đơn giản nhất cho tất cả các tùy chọn khác nhau, cũng như việc thử nghiệm từng cá nhân đơn giản hơn. Khả năng của hệ điều hành là do tính chất của nó, rất đặc trưng cho hệ điều hành) và sau đó thực hiện hầu hết phần còn lại của mã của bạn dưới dạng các đối tượng/tập tin được chia sẻ Ngoài ra bạn có thể chia sẻ cùng một tệp cho hai kiến ​​trúc khác nhau nếu yo Hiện tại bạn không cảm thấy có bất kỳ điểm nào bằng cách sử dụng các khả năng khác nhau.

Tôi cho rằng thực thi chính có khả năng bị buộc phải đưa ra lựa chọn cụ thể để bạn có thể thấy điều gì xảy ra với phiên bản 'ít hơn' trên máy có khả năng hơn (hoặc lỗi nào xảy ra nếu bạn thử một thứ khác).

khả năng khác cho mô hình này là:

  • liên kết tĩnh với các phiên bản khác nhau của runtimes tiêu chuẩn (đối với những người có/không an toàn thread) và sử dụng chúng một cách thích hợp nếu bạn đang chạy mà không cần bất kỳ khả năng SMP/SMT.
  • Phát hiện nếu có nhiều lõi và liệu chúng có thực hay siêu luồng (cũng liệu hệ điều hành có biết lịch trình hiệu quả trong những trường hợp đó không)
  • kiểm tra hiệu suất của những thứ như hẹn giờ hệ thống/bộ tính giờ hiệu suất cao và sử dụng mã được tối ưu hóa cho hành vi này, nếu bạn làm bất cứ điều gì mà bạn tìm một khoảng thời gian nhất định để hết hạn và do đó có thể biết được mức độ chi tiết tốt nhất có thể của bạn.
  • Nếu bạn muốn tối ưu hóa lựa chọn mã dựa trên kích thước bộ nhớ cache/tải khác trên hộp. Nếu bạn đang sử dụng các vòng chưa được kiểm định thì các tùy chọn mở rộng tích cực hơn có thể phụ thuộc vào việc có một bộ nhớ cache mức 1/2 nhất định.
  • Biên dịch có điều kiện để sử dụng đôi/nổi tùy thuộc vào kiến ​​trúc. Ít quan trọng trên phần cứng intel nhưng nếu bạn đang nhắm mục tiêu một số CPU của ARM có một số phần cứng hỗ trợ điểm nổi thực tế và những người khác yêu cầu thi đua. Mã tối ưu sẽ thay đổi rất nhiều, ngay cả khi bạn chỉ sử dụng trình biên dịch có điều kiện thay vì sử dụng trình biên dịch tối ưu hóa (1).
  • Sử dụng phần cứng đồng bộ như thẻ đồ họa có khả năng CUDA.
  • phát hiện ảo hóa và thay đổi hành vi (có lẽ cố gắng để tránh hệ thống tập tin viết)

Đối với làm này kiểm tra xem bạn có một vài lựa chọn, là hữu ích nhất trên Intel là các hướng dẫn cpuid .

Ngoài ra tái thực hiện/cập nhật hiện có sử dụng tài liệu có sẵn trên các tính năng bạn cần.

Khá nhiều tài liệu riêng biệt để làm việc ra làm thế nào để phát hiện điều:

Một phần lớn những gì bạn sẽ trả tiền trong thư viện CPU-Z là ai đó làm tất cả những điều này (và những vấn đề nhỏ khó chịu liên quan) cho bạn.


  1. hãy cẩn thận với điều này - đó là khó khăn để đánh bại các trình biên dịch tối ưu hóa đàng hoàng về vấn đề này
6

Bạn có thể sử dụng tập lệnh không?

Bạn có thể phát hiện CPU bằng tập lệnh và tự động tải tệp thực thi được tối ưu hóa nhất cho kiến ​​trúc. Nó cũng có thể chọn phiên bản 32/64 bit.

Nếu bạn đang sử dụng Linux, bạn có thể truy vấn các cpu với

cat /proc/cpuinfo 

Bạn có thể có thể làm điều này với một loạt bash/perl/python script hoặc cửa sổ kịch bản trên cửa sổ. Có thể bạn không muốn ép buộc người dùng cài đặt một công cụ tập lệnh. Một trong những hoạt động trên hệ điều hành ra khỏi hộp IMHO sẽ là tốt nhất.

Thực tế, trên các cửa sổ bạn có thể muốn viết một ứng dụng C# nhỏ để bạn có thể dễ dàng truy vấn kiến ​​trúc hơn. Ứng dụng C# chỉ có thể sinh ra bất kỳ tệp thực thi nào là nhanh nhất.

Hoặc bạn có thể đặt các phiên bản mã khác nhau của bạn trong một đối tượng của dll hoặc chia sẻ, sau đó tự động tải chúng dựa trên kiến ​​trúc được phát hiện. Miễn là chúng có cùng chữ ký cuộc gọi thì nó sẽ hoạt động.

+0

Bạn thực sự không cần script để phát hiện CPU - bạn có thể làm điều đó với hệ điều hành gốc phụ thuộc vào hệ thống cuộc gọi. –

+0

Nhưng nếu bạn sử dụng tập lệnh, nó sẽ trở thành di động trên các kiến ​​trúc bit của hệ điều hành và 64/32. –

+2

Xem xét rằng anh ấy đã viết (khá thận trọng) mã phụ thuộc vào hệ điều hành, tôi không nghĩ rằng nó là cần thiết để đảm bảo rằng hệ điều hành phát hiện là di động. Mặc dù có một phần của ứng dụng được di chuyển có lẽ sẽ làm cho mọi thứ dễ dàng hơn. – Brian

16

Có thể. Biên dịch tất cả các phiên bản được tối ưu hóa khác nhau thành các thư viện động khác nhau với một điểm vào chung và cung cấp một thư mục thực thi để tải và chạy thư viện chính xác tại thời điểm chạy, thông qua điểm nhập, tùy thuộc vào tệp cấu hình hoặc thông tin khác.

+0

Cảm ơn! Bạn có thể có một số con trỏ cụ thể hơn làm thế nào để biên dịch theo cách đó? Và làm thế nào stub sẽ giống như thế nào? –

+0

Dưới cửa sổ, bạn có thể kích hoạt một DLL 64 bit từ một quy trình 32 bit không? Tôi không nghĩ rằng bạn có thể .. nhưng rất thích xem làm thế nào bạn có thể làm điều đó :) – Goz

+0

Sau đó, người ta có thể cung cấp một lớp khác: một bộ tải 32-bit, có phát hiện chính nó chạy trên một vòm 64-bit, exec'ed Á hậu 64 bit, người lần lượt tải thư viện 64 bit. –

3

Vì bạn đề cập đến bạn đang sử dụng GCC, tôi sẽ giả sử mã của bạn là bằng C (hoặc C++).

Neil Butterworth đã đề xuất tạo các thư viện động riêng biệt, nhưng yêu cầu cân nhắc nhiều nền tảng không quan trọng (tải thư viện động theo cách thủ công khác với Linux, Windows, OSX, v.v.).

Một giải pháp rẻ là chỉ cần viết tất cả các biến thể của bạn bằng cách sử dụng tên duy nhất và sử dụng một con trỏ hàm để chọn một biến thích hợp khi chạy.

Tôi nghi ngờ thêm dereference gây ra bởi con trỏ hàm sẽ được khấu hao theo công việc thực tế bạn đang làm (nhưng bạn sẽ muốn xác nhận điều đó).

Ngoài ra, việc tối ưu hóa trình biên dịch khác nhau có thể sẽ yêu cầu các tệp .c/.cpp khác nhau, cũng như một số công cụ xây dựng của bạn. Nhưng nó có lẽ ít công việc tổng thể hơn so với các thư viện riêng biệt (cần thiết này đã có trong một hình thức này hay cách khác).

+0

Đây là một gợi ý khủng khiếp và bạn sẽ phải là hạt để sử dụng nó. Tôi không thường đưa ra những tuyên bố như vậy, nhưng trong trường hợp này tôi cảm thấy tôi phải làm vậy. Đừng làm điều này. –

+0

Tôi hoàn toàn không muốn có các tệp .cpp khác nhau. Đó là một cơn ác mộng để duy trì! Nếu tôi có một số tối ưu hóa cho các nền tảng cụ thể trong mã của tôi, tôi nghĩ ifdefs sẽ phục vụ tôi. –

+0

OK, tôi cần như tôi cảm thấy để bảo vệ bản thân mình một chút ở đây, xem xét sức mạnh của những ý kiến. Trước tiên, sự hiểu biết của tôi là bạn muốn biên dịch các phiên bản khác nhau của một thói quen toán học chuyên sâu cho cùng một kiến ​​trúc (ví dụ x86), nhưng với các triển khai/tối ưu khác nhau (SSE, -O1/O2/O3, v.v.). Tôi tin rằng "-tune" và "-mfpmath" của GCC không thể được kiểm soát bởi bộ tiền xử lý, vì vậy bạn có thể phải biên dịch lại cùng một tệp .cpp để tạo các tệp .o khác nhau. Đề nghị của Neil là để có những kết thúc trong thư viện năng động khác nhau. Tôi đã có tất cả chúng trong cùng một nhị phân (cont.). – jhoule

5

Hãy xem liboil: http://liboil.freedesktop.org/wiki/. Nó có thể tự động chọn việc triển khai các tính toán liên quan đến đa phương tiện tại thời gian chạy. Bạn có thể tìm thấy bạn có thể liboil chính nó và không chỉ kỹ thuật của nó.

3

Vì bạn không chỉ định liệu bạn có giới hạn về số lượng tệp hay không, tôi đề xuất một giải pháp khác: biên dịch 5 tệp thi hành và sau đó tạo tệp thực thi thứ sáu khởi chạy nhị phân thích hợp. Dưới đây là một số giả, dành cho Linux

int main(int argc, char* argv[]) 
{ 
    char* target_path[MAXPATH]; 
    char* new_argv[]; 
    char* specific_version = determine_name_of_specific_version(); 
    strcpy(target_path, "/usr/lib/myapp/versions"); 
    strcat(target_path, specific_version); 

    /* append NULL to argv */ 
    new_argv = malloc(sizeof(char*)*(argc+1)); 
    memcpy(new_argv, argv, argc*sizeof(char*)); 
    new_argv[argc] = 0; 
    /* optionally set new_argv[0] to target_path */ 

    execv(target_path, new_argv); 
} 

Về phía cộng, phương pháp này cho phép để cung cấp cho người dùng một cách minh bạch với cả hai mã nhị phân 32-bit và 64-bit, không giống như bất kỳ phương pháp thư viện mà đã được đề xuất. Về phía trừ, không có execv trong Win32 (nhưng một mô phỏng tốt trong Cygwin); trên Windows, bạn phải tạo một quy trình mới, thay vì thực thi lại quy trình hiện tại.

1

Bạn đã đề cập đến trình biên dịch Intel. Điều đó thật buồn cười, bởi vì nó có thể làm một cái gì đó như thế này theo mặc định. Tuy nhiên, có một nhược điểm. Trình biên dịch Intel không chèn kiểm tra cho chức năng SSE phê duyệt. Thay vào đó, họ đã kiểm tra xem bạn có một chip Intel cụ thể không. Vẫn sẽ có một trường hợp mặc định chậm. Kết quả là, các CPU AMD sẽ không nhận được các phiên bản tối ưu hóa SSE phù hợp. Có những hacks nổi xung quanh sẽ thay thế kiểm tra của Intel bằng một kiểm tra SSE thích hợp.

Sự khác biệt 32/64 bit sẽ yêu cầu hai tệp thực thi. Cả hai định dạng ELF và PE đều lưu trữ thông tin này trong phần đầu tiên. Không quá khó để khởi động phiên bản 32 bit theo mặc định, kiểm tra xem bạn có đang sử dụng hệ thống 64 bit hay không và sau đó khởi động lại phiên bản 64 bit. Nhưng nó có thể dễ dàng hơn để tạo một liên kết tượng trưng thích hợp tại thời điểm cài đặt.

+0

Chức năng Intel này được gọi là gì? Hoặc bạn có liên kết đến tài liệu và đề cập đến hack? –

1

Cho phép phá vỡ các vấn đề xuống để hai bộ phận cấu thành của nó. 1) Tạo mã tối ưu hóa phụ thuộc vào nền tảng và 2) xây dựng trên nhiều nền tảng.

Vấn đề đầu tiên khá đơn giản. Đóng gói mã phụ thuộc vào nền tảng trong một tập hợp các hàm. Tạo một triển khai khác nhau của từng chức năng cho mỗi nền tảng. Đặt từng triển khai vào tệp hoặc tập hợp tệp của riêng nó. Nó dễ dàng nhất cho hệ thống xây dựng nếu bạn đặt mã của mỗi nền tảng trong một thư mục riêng biệt.

Đối với phần hai, tôi đề nghị bạn xem các Atuotools Gnu (Automake, AutoConf và Libtool). Nếu bạn đã từng tải xuống và xây dựng một chương trình GNU từ mã nguồn bạn biết bạn phải chạy ./configure trước khi chạy. Mục đích của kịch bản cấu hình là 1) xác minh rằng hệ thống của bạn có tất cả các thư viện và tiện ích cần thiết cần xây dựng và chạy chương trình và 2) tùy chỉnh Makefiles cho nền tảng đích. Autotools là tập hợp các tiện ích để tạo kịch bản cấu hình.

Sử dụng autoconf, bạn có thể tạo các macro nhỏ để kiểm tra xem máy có hỗ trợ tất cả các hướng dẫn CPU mà mã phụ thuộc nền tảng của bạn cần hay không. Trong hầu hết các trường hợp, các macro đã tồn tại, bạn chỉ cần sao chép chúng vào tập lệnh autoconf của bạn. Sau đó, automake và autoconf có thể thiết lập Makefiles để thực hiện việc triển khai thích hợp.

Tất cả điều này hơi nhiều để tạo ví dụ tại đây. Phải mất một ít thời gian để học. Nhưng tài liệu là tất cả ra khỏi đó. Thậm chí còn có free book có sẵn trực tuyến. Và quy trình này có thể áp dụng cho các dự án tương lai của bạn. Đối với hỗ trợ đa nền tảng, đây thực sự là cách mạnh mẽ nhất và dễ nhất để đi, tôi nghĩ vậy. Rất nhiều đề xuất được đăng trong các câu trả lời khác là những thứ mà Autotools đề cập đến (phát hiện CPU, static & hỗ trợ thư viện được chia sẻ) mà không cần phải suy nghĩ quá nhiều. Các nếp nhăn duy nhất bạn có thể phải đối phó với là tìm ra nếu Autotools có sẵn cho MinGW. Tôi biết họ là một phần của Cygwin nếu bạn có thể đi tuyến đường đó thay thế.

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