#ifdef #ifndef trong Java


106

Tôi nghi ngờ nếu có cách nào tạo điều kiện thời gian biên dịch trong Java như #ifdef #ifndef trong C ++.

Vấn đề của tôi là có một thuật toán được viết bằng Java và tôi có thời gian chạy khác nhau được cải thiện cho thuật toán đó. Vì vậy, tôi muốn đo lường thời gian tôi tiết kiệm được khi sử dụng mỗi cải tiến.

Ngay bây giờ tôi có một tập hợp các biến boolean được sử dụng để quyết định trong thời gian chạy, cải tiến nào nên được sử dụng và biến nào không. Nhưng ngay cả việc kiểm tra các biến đó cũng ảnh hưởng đến tổng thời gian chạy.

Vì vậy, tôi muốn tìm ra cách để quyết định trong thời gian biên dịch nên biên dịch và sử dụng những phần nào của chương trình.

Có ai đó biết một cách để làm điều đó trong Java. Hoặc có thể ai đó biết rằng không có cách nào như vậy (nó cũng sẽ hữu ích).

Câu trả lời:


126
private static final boolean enableFast = false;

// ...
if (enableFast) {
  // This is removed at compile time
}

Các điều kiện như hiển thị ở trên được đánh giá tại thời điểm biên dịch. Nếu thay vào đó bạn sử dụng cái này

private static final boolean enableFast = "true".equals(System.getProperty("fast"));

Sau đó, bất kỳ điều kiện nào phụ thuộc vào enableFast sẽ được trình biên dịch JIT đánh giá. Chi phí cho việc này là không đáng kể.


Giải pháp này tốt hơn là của tôi. Khi tôi cố gắng khởi tạo các biến với giá trị bên ngoài được đặt trước, thời gian chạy đã quay trở lại 3 giây. Nhưng khi tôi xác định các biến là biến lớp tĩnh (và không phải là biến cục bộ của hàm) thì thời gian chạy trả về 1 giây. Cảm ơn đã giúp đỡ.
jutky

6
IIRC, điều này thậm chí đã hoạt động trước khi Java có trình biên dịch JIT. javacTôi nghĩ mã đã bị xóa . Điều này chỉ hoạt động nếu biểu thức cho (nói) enableFastlà một biểu thức hằng số thời gian biên dịch.
Stephen C

2
Có, nhưng điều kiện này phải nằm trong một phương thức, đúng không? Còn về trường hợp chúng ta có một loạt các Chuỗi cuối cùng tĩnh riêng tư mà chúng ta muốn đặt. (ví dụ: một tập hợp các URL máy chủ được đặt khác nhau cho sản xuất so với dàn)
tomwhipple vào

3
@tomwhipple: true, cộng với điều này không cho phép bạn làm điều gì đó như: private void foo(#ifdef DEBUG DebugClass obj #else ReleaseClass obj #endif )
Zonko

3
những gì về nhập khẩu (ví dụ, liên quan đến classpath)?
n611x007 Ngày

44

javac sẽ không xuất mã đã biên dịch không thể truy cập được. Sử dụng biến cuối cùng được đặt thành giá trị không đổi cho của bạn #definevà một ifcâu lệnh bình thường cho #ifdef.

Bạn có thể sử dụng javap để chứng minh rằng mã không thể truy cập được không có trong tệp lớp đầu ra. Ví dụ: hãy xem xét đoạn mã sau:

public class Test
{
   private static final boolean debug = false;

   public static void main(String[] args)
   {
       if (debug) 
       {
           System.out.println("debug was enabled");
       }
       else
       {
           System.out.println("debug was not enabled");
       }
   }
}

javap -c Test đưa ra kết quả sau, chỉ ra rằng chỉ một trong hai đường dẫn được biên dịch trong (và câu lệnh if thì không):

public static void main(java.lang.String[]);
  Code:
   0:   getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
   3:   ldc     #3; //String debug was not enabled
   5:   invokevirtual   #4; //Method java/io/PrintStream.println:(Ljava/lang/String;)V
   8:   return

2
Đây có phải là javac cụ thể hay hành vi này thực sự được đảm bảo bởi JLS?
Pacerier

@pacerier, tôi không biết liệu điều này có được đảm bảo bởi JLS hay không, nhưng nó đúng với mọi trình biên dịch java mà tôi đã xem từ những năm 90, ngoại trừ có thể có trước 1.1.7 và chỉ vì tôi không kiểm tra nó sau đó.

12

Tôi nghĩ rằng tôi đã tìm ra giải pháp, Nó đơn giản hơn nhiều.
Nếu tôi xác định các biến boolean bằng công cụ sửa đổi "cuối cùng" thì chính trình biên dịch Java sẽ giải quyết được vấn đề. Bởi vì nó biết trước kết quả của việc kiểm tra điều kiện này là gì. Ví dụ mã này:

    boolean flag1 = true;
    boolean flag2 = false;
    int j=0;
    for(int i=0;i<1000000000;i++){
        if(flag1)
            if(flag2)
                j++;
            else
                j++;
        else
            if(flag2)
                j++;
            else
                j++;
    }

chạy khoảng 3 giây trên máy tính của tôi.
Và cái này nữa

    final boolean flag1 = true;
    final boolean flag2 = false;
    int j=0;
    for(int i=0;i<1000000000;i++){
        if(flag1)
            if(flag2)
                j++;
            else
                j++;
        else
            if(flag2)
                j++;
            else
                j++;
    }

chạy khoảng 1 giây. Đồng thời mã này mất

    int j=0;
    for(int i=0;i<1000000000;i++){
        j++;
    }

1
Điều đó thật thú vị. Có vẻ như JIT đã hỗ trợ biên dịch có điều kiện! Nó có hoạt động nếu những trận chung kết đó nằm trong một lớp khác hoặc một gói khác không?
joeytwiddle

Tuyệt quá! Sau đó, tôi tin rằng đây phải là một tối ưu hóa thời gian chạy, mã không thực sự bị loại bỏ trong thời gian biên dịch. Điều đó tốt miễn là bạn sử dụng một máy ảo thuần thục.
joeytwiddle

@joeytwiddle, Từ khóa là "miễn là bạn sử dụng" một máy ảo thuần thục.
Pacerier

2

Chưa bao giờ sử dụng nó, nhưng điều này tồn tại

JCPP là một triển khai Java thuần túy, tuân thủ, độc lập, hoàn chỉnh của bộ tiền xử lý C. Nó được thiết kế để sử dụng cho những người viết trình biên dịch kiểu C trong Java bằng cách sử dụng các công cụ như sablecc, antlr, JLex, CUP, v.v. Dự án này đã được sử dụng để xử lý thành công phần lớn mã nguồn của thư viện GNU C. Kể từ phiên bản 1.2.5, nó cũng có thể xử lý trước thư viện Apple Objective C.

http://www.anarres.org/projects/jcpp/


1
Tôi không chắc điều này phù hợp với nhu cầu của tôi. Mã của tôi được viết bằng Java. Có thể bạn đang đề xuất tôi lấy nguồn của họ và sử dụng chúng để xử lý trước mã của tôi?
jutky

2

Nếu bạn thực sự cần biên dịch có điều kiện và bạn sử dụng Ant , bạn có thể lọc mã của mình và thực hiện tìm kiếm và thay thế trong đó.

Ví dụ: http://weblogs.java.net/blog/schaefa/archive/2005/01/how_to_do_condi.html

Theo cách tương tự, ví dụ, bạn có thể viết một bộ lọc để thay thế LOG.debug(...);bằng /*LOG.debug(...);*/. Điều này vẫn sẽ thực thi nhanh hơn nhiều if (LOG.isDebugEnabled()) { ... }thứ, chưa kể là ngắn gọn hơn cùng một lúc.

Nếu bạn sử dụng Maven , có một tính năng tương tự được mô tả ở đây .


2

Manifold cung cấp một bộ tiền xử lý Java được tích hợp đầy đủ (không có bước xây dựng hoặc nguồn được tạo). Nó chỉ nhắm mục tiêu đến việc biên dịch có điều kiện và sử dụng các chỉ thị kiểu C.

Bộ tiền xử lý Java của Manifold


1

Sử dụng Factory Pattern để chuyển đổi giữa các triển khai của một lớp?

Thời gian tạo đối tượng không phải là một mối quan tâm bây giờ phải không? Khi được tính trung bình trong một khoảng thời gian dài, thành phần lớn nhất của thời gian dành cho thuật toán chính bây giờ phải không?

Nói một cách chính xác, bạn không thực sự cần một bộ xử lý trước để làm những gì bạn muốn đạt được. Tất nhiên có lẽ có nhiều cách khác để đáp ứng yêu cầu của bạn hơn cách mà tôi đã đề xuất.


Những thay đổi là rất nhỏ. Giống như thử nghiệm một số điều kiện để biết trước kết quả được yêu cầu thay vì tính toán lại. Vì vậy, chi phí của cuộc gọi đến hàm có thể không phù hợp với tôi.
jutky

0
final static int appFlags = context.getApplicationInfo().flags;
final static boolean isDebug = (appFlags & ApplicationInfo.FLAG_DEBUGGABLE) != 0
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.