2015-12-22 51 views
3

我有這樣一個簡單的嵌套結構。 (這是簡化的實際問題的版本他們中的一些實際使用的hash_set。)Java:通過大量半常數標誌檢查來優化循環循環?

class A{ AF a_field; B[] bs;} 
class B{ BF b_field; C[] cs;} 
class C{ CF c_field; D[] ds;} 
class D{ DF d_field; } 
static A[] as; // loosely speaking, it has about 4000 D 

一個僞碼法「F」看起來可能是複雜的,但它是很容易理解: -

int f(int TYPE){ //TYPE is 0 - 40 
    int retur=0; 
    for(int n=0;n<as.length;n++){ 
    . if(some cheap condition of TYPE){ //use only "TYPE" , cheap (1) 
    . . as[n].a_field.doExpensiveAA(); //expensive OPERATION, use only "a_field" (Ex retur+=a_field) 
    . }else if(some cheap condition of TYPE){ //use only "TYPE" (2) 
    . . as[n].a_field.doExpensiveAB(); //expensive OPERATION, use only "a_field" (Ex retur-=a_field) 
    . } // .... and a lot of them 
    . for(int m=0;m<bs.length;m++){ 
    . . if(some cheap condition of TYPE){ //use only "TYPE" (3) 
    . . . as[n].bs[m].b_field.doExpensiveBA(); (*)//use only "b_field" 
    . . }else if(some cheap condition of TYPE){//use only "TYPE" (4) 
    . . . as[n].bs[m].b_field.doExpensiveBB(); (/) //use only "b_field" 
    . . } // .... and a lot of them 
    . . for(..cs ..){...for(...ds...) {...}...} 
    . } 
    } 
} 

(我拼命地添加縮進。)

「f」 被稱爲在每個時間步長: -

public static void caller(){ 
    for(int n=0;n<40;n++){ f(n); } 
} 

請注意,TYPE只是用於條件檢查的變量,對於單個「f」調用而言它是不變的。

雖然每個條件檢查確實很便宜,但是當它位於最內層循環時,它會花費很多CPU週期。如何優化「呼叫者」和/或「F」?

我的想法是

  1. 展開「F」爲每一個「類型」,但會有很多骯髒的代碼是很難維持......這樣......

    (); f2(); f3(); f1(); f2(); f3(); f1(); f2(); f3(); ... f40(); }

  2. 使用開關盒!它比if-else更快,但是0到40的開關箱很醜。條件不能像「檢查TYPE的奇數/偶數」那樣分組,因此代碼的可維護性較低。

編輯1(應答帕塔Sarathi戈什):檢查位只是例子,所以位的指標是不重要的,它甚至可以「> 8」,只是要注意,所有的條件是依賴於類型。

編輯2:+ - * /只是一個例子,它是一個使用a_field,b_field等的任意函數(真實情況是在Opengl中設置3D紋理或系統變量)... cs。 ..和... ds ...是類似但不同的昂貴操作。

編輯3:添加信息 「A []爲」 包含有關4000 d

編輯4(回答帕塔Sarathi戈什):從int編輯xxx_field的類型,以顯示它們是不同的類。

+1

在第二個for循環使用的內部使用了'if(「TYPE的最後一位是1」)和'else if(「TYPE的第二個最後一位是0」)是否有任何錯誤? –

+1

而且,您還將'...... as ......'用作'retur + = as [n] .a_field'和'retur * = as [n] .a_field'。但'retur- = as [n] .bs [m] .b_field'和'retur/= as [n] .bs [m] .b_field'爲'... bs ...'。那麼對於'..cs ...'和其他所有人來說,會是什麼? –

+2

這意味着你只有相同的結構數據(包裝在不同的類中)。但是所有這些數據都是相互嵌套的。現在取決於一個標誌變量說類型,你需要在不同的水平上執行不同的操作。但是對於單一類型,您需要針對特定​​級別執行特定操作,而不是其他級別的權限? –

回答

2

您需要將函數作爲參數傳遞給遞歸函數。我在準備基本算法的時候看到這個post

您還需要使用自定義標記接口以相同的遞歸方法傳遞這些不同類型的數據。

這是您的示例程序。

import java.util.List; 
import java.util.ArrayList; 



/** The Invoker class */ 
class Switch { 
    List<Command> commandList = new ArrayList<Command>(); 
    Switch() { 
     commandList.add(new IncreementCommand()); //Level 1 Operation 
     commandList.add(new MultipleCommand()); 
     commandList.add(new DecrementCommand()); 
     //If level 4 need same operation like level 1 
     commandList.add(new IncreementCommand()); 

    } 

    public int execute(CustomMarker a, int lvl){ 
     int rtr = 0; 
     Command cmd = commandList.get(lvl-1); //Level 1 means 1st operation 
     return execute(a, lvl, rtr, cmd); 
    } 


    /** The Command interface */ 
    interface Command { 
     int execute(int data, int rtr); 
    } 

    private int execute(CustomMarker a, int lvl, int rtr, Command cmd){ 
     if(lvl == 0){ 
      return cmd.execute(a.getData(), rtr); 
     } 
     else { 
      lvl--; 
      if(a.getSubData() != null && a.getSubData().length>0){ 
       for (int i = 0; i < a.getSubData().length; i++) { 
       rtr = execute(a.getSubData()[i], lvl, rtr, cmd); 
       } 
      } 
      return rtr; 
     } 
    } 

    //Inner classes 
    class IncreementCommand implements Command { 
     @Override // Command 
     public int execute(int data, int rtr) { 
      return rtr+data; 
     } 
    } 

    /** The Command for turning off the light - ConcreteCommand #2 */ 
    class MultipleCommand implements Command { 
     @Override // Command 
     public int execute(int data, int rtr) { 
      return rtr*data; 
     } 
    } 

    /** The Command for turning off the light - ConcreteCommand #2 */ 
    class DecrementCommand implements Command { 
     @Override // Command 
     public int execute(int data, int rtr) { 
      return rtr-data; 
     } 
    } 
} 

/** The Custom Marker interface */ 
interface CustomMarker { 
    //It will return your int a_field or b_field 
    public int getData(); 
    public CustomMarker[] getSubData(); 
} 


//Level 1 
class A implements CustomMarker { int a_field; B[] bs; 
    public int getData() { 
     return a_field; 
    } 
    public CustomMarker[] getSubData() { 
     return bs; 
    } 
} 
//Level 2 
class B implements CustomMarker { int b_field; C[] cs; 
    public int getData() { 
     return b_field; 
    } 
    public CustomMarker[] getSubData() { 
     return cs; 
    } 
} 
//Level 3 
class C implements CustomMarker { int c_field; D[] ds; 
    public int getData() { 
     return c_field; 
    } 
    public CustomMarker[] getSubData() { 
     return ds; 
    } 
} 
//Level 4 
class D implements CustomMarker { int d_field; 
    public int getData() { 
     return d_field; 
    } 
    public CustomMarker[] getSubData() { 
     return null; 
    } 
} 


/* The test class or client */ 
public class TestClass { 
    static A[] as; 
    public static void main(String[] args){ 



     CustomMarker topLevel = new CustomMarker() { 
     @Override 
     public int getData() { 
      // TODO Auto-generated method stub 
      return 0; 
     } 
     @Override 
     public CustomMarker[] getSubData() { 
      return as; 
     } 
     }; 

     Switch mySwitch = new Switch(); 
     for(int n=1;n<=3;n++){ 
      mySwitch.execute(topLevel, n); 
     } 
    } 
} 

在這個程序中我已經使用相同的數據類型爲a_field和爲int。 但是你可以用Object來試試它。所以在每個操作中執行block typecast它。如果程序可以正確處理關卡及其類型,則不需要在類型轉換之前進行檢查。

我已最小化操作到41次只。 40操作和+ 1開關(操作控制器)。

編輯:更新了Switch類的執行公共方法,複製粘貼的錯誤在那裏。

+0

在準備答案時,您是否可以避免創建(「新」)很多對象?像C++那樣傳遞函數指針似乎很酷,但是我沒有找到一種沒有很多「新」調用的方法。我相信「新」會讓Java變慢。 A []「as」有大約4000個D對象,在每個時間步中創建4000個函數指針可能不是那麼好...不確定...謝謝。 – javaLover

+0

更新了一個示例程序,讓我知道如果你有難以理解的邏輯。 –

+0

你也可以通過反射來完成這個程序。爲創建40個不同的內部類而創建,你可以在Switch類中創建40個方法。然後你可以在CollandList(String of List)中獲取這些方法的名稱,然後通過Reflection getMethod找到該方法。然後執行它。但我認爲用反思進行工作是昂貴的。 –