2016-12-28 66 views
4

我們正在對LLVM庫進行研究,我們發現IR庫有時可以調用多達29個方法調用的堆棧。是否產生深層調用堆棧的代碼被視爲反模式?

有時,當我在iOS框架中看到一些崩潰時,我也觀察到相當深的調用堆棧。

我的問題是,我們是否可以推斷一個代碼的設計是否可能會出現問題,這種代碼的設計如此深度。

下面是一個例子:

/usr/local/LLVM/llvm/unittests/IR/AttributesTest.cpp:54 
    /usr/local/LLVM/llvm/lib/IR/LLVMContext.cpp:162 
    /usr/local/LLVM/llvm/lib/IR/LLVMContext.cpp:162 
     /usr/local/LLVM/llvm/lib/IR/LLVMContextImpl.cpp:54 
     /usr/local/LLVM/llvm/lib/IR/LLVMContextImpl.cpp:59 
      /usr/local/LLVM/llvm/lib/IR/Module.cpp:60 
      /usr/local/LLVM/llvm/lib/IR/Module.cpp:62 
       /usr/local/LLVM/llvm/lib/IR/Module.cpp:456 
       /usr/local/LLVM/llvm/lib/IR/Function.cpp:350 
        /usr/local/LLVM/llvm/lib/IR/BasicBlock.cpp:98 
        /usr/local/LLVM/llvm/include/llvm/ADT/ilist.h:282 
         /usr/local/LLVM/llvm/include/llvm/ADT/ilist.h:267 
         /usr/local/LLVM/llvm/lib/IR/SymbolTableListTraitsImpl.h:76 
          /usr/local/LLVM/llvm/lib/IR/BasicBlock.cpp:90 
          /usr/local/LLVM/llvm/lib/IR/SymbolTableListTraitsImpl.h:58 
           /usr/local/LLVM/llvm/lib/IR/ValueSymbolTable.cpp:75 
           /usr/local/LLVM/llvm/lib/IR/ValueSymbolTable.cpp:47 
            /usr/local/LLVM/llvm/include/llvm/Support/Casting.h:132 
            /usr/local/LLVM/llvm/include/llvm/Support/Casting.h:112 
             /usr/local/LLVM/llvm/include/llvm/Support/Casting.h:122 
             /usr/local/LLVM/llvm/include/llvm/Support/Casting.h:96 
              /usr/local/LLVM/llvm/include/llvm/IR/Value.h:777 
              /usr/local/LLVM/llvm/include/llvm/Support/Casting.h:132 
               /usr/local/LLVM/llvm/include/llvm/Support/Casting.h:122 
               /usr/local/LLVM/llvm/include/llvm/Support/Casting.h:75 
                /usr/local/LLVM/llvm/include/llvm/IR/Value.h:771 
                /usr/local/LLVM/llvm/include/llvm/Support/Casting.h:132 
                 /usr/local/LLVM/llvm/include/llvm/Support/Casting.h:122 
                 /usr/local/LLVM/llvm/include/llvm/Support/Casting.h:75 
                  /usr/local/LLVM/llvm/include/llvm/IR/Value.h:759 

P.S.示例調用堆棧實際上是由LLVMContext類的析構函數生成的:LLVMContext::~LLVMContext()。這是來自Java世界的一篇非常古老的帖子的另一個例子:Java call stack – from HTTP upto JDBC as a picture

+2

取決於「深」的尺度。例如,對於樹遍歷來說,使用樹深度來生成調用堆棧比例是非常標準的。 – user2357112

+0

它在旁觀者的眼中。也許虛擬方法是一種反模式。也許甚至C++或者面向對象都是反模式。 – wildplasser

+0

除非我們保證遞歸深度,否則樹遍歷和其他遞歸模式不應該使用調用堆棧進行遞歸,而是使用迭代實現,以避免爆炸堆棧大小限制。這在Clang/LLVM中是有效的。 – Joky

回答

5

我的問題是,我們是否可以推論,如果有可能是一些錯誤的,在深度的那麼大級別自稱爲代碼的設計。

我要去無路可退,並說「是」,但也有你的問題和回答的問題。

這個概念關於是否可能有的原因是沒有太多掛在你的帽子。你可以推斷一個循環是否終止;你可以證明它確實或沒有。你可以推斷是否存在競爭條件。你不能推斷是否存在

沒有標準,沒有度量標準,沒有權威會告訴你調用堆棧太深。請記住幾乎任何調用堆棧都是可以避免的:調用堆棧是庫的「因式分解」(如果您願意的話)的人工產物。可以想象用宏或C++模板替換函數調用。相同的邏輯效果,稍低的指令數量。也許更便宜,因爲線上或更昂貴,因爲重複的代碼。但至少堆棧指針沒有改變!

所以我把你的問題解釋爲:是一個很大的調用堆棧,相對於已實現的功能,是否需要仔細檢查代碼以避免不必要的複雜性?對此,我說是。

面對類似於你所描述的情況,我會懷疑這些調用中的一些是否爲「經理」函數:某種通用包裝函數不會做太多。我記得幾年前讀過一個編組庫,其中調用堆棧寫入(2)的深度是14。除了將數據混洗到另一個抽象概念之外,大部分代碼都沒有做任何事情。

沒有巧合:該庫和你的都是C++。 C++使得隱式函數調用變得容易,而析構函數就是一個例子。如果你把這個析構函數寫成C函數,我敢打賭它會很長很平坦。在釋放內存之前,析構函數也容易做大量的「清理」工作;在C中,你可能只需要幾次調用免費(3),並且已經完成了它。

所以這不是真正的調用堆棧深度本身這是一個問題。但是你的本能是正確的:一個超過少量功能的大型調用堆棧表明一種超組織的意大利麪代碼。重新查看功能當然不會有什麼壞處,並且可能會尋找方法來減少抽象的數量。

+0

相關/切線「*沒有標準,沒有度量標準,沒有權威會告訴你調用堆棧太深。*」:...除非你在討論必須使用4kiB或8kiB運行的內核代碼堆棧大小([例如在Linux上](http://elinux.org/Kernel_Small_Stacks))。但這不僅僅是嵌套的層次,也是特定功能用於自動存儲的空間。 –