生成深度调用堆栈的代码是否被视为反模式?

时间:2016-12-28 23:05:40

标签: llvm callstack anti-patterns

我们正在围绕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 个答案:

答案 0 :(得分:6)

  

我的问题是,我们是否可以推断一个代码的设计是否存在问题,而这个代码的设计自称是如此之大。

我准备出门并说“是”和“#34;”但是你的问题和答案都有问题。

关于是否可能的概念的原因并不重要。你可以推断循环是否终止;你可以证明它有或没有。您可以判断是否存在竞争条件。您无法推断可能是否存在。

没有标准,没有指标,没有权限会告诉你调用堆栈有多深。请记住几乎任何调用堆栈是可以避免的:调用堆栈是"分解" (如果你愿意的话)你的图书馆。可以想象用宏或C ++模板替换函数调用。逻辑效果相同,指令数略低。也许更便宜,因为在线,或更昂贵,因为重复的代码。但至少堆栈指针没有变化!

所以我将你的问题解释为:是一个大型调用堆栈,相对于已实现的功能,有理由仔细检查代码是否存在不必要的复杂性?对此,我说是的。

面对你所描述的情况,我想知道其中一些电话是否是"经理"函数:某种通用的包装器并不是很有用。我记得几年前读过一个编组库,其中(2)的调用堆栈深度为14。除了将数据转换为另一个抽象之外,大多数代码都没有做任何事情。

不巧合:那个库和你的库都是C ++。 C ++使得隐式函数调用变得容易,而析构函数就是一个例子。如果你把析构函数写成C函数,我敢打赌它会长而扁平。析构函数也很容易做很多"清理"就在释放记忆之前;在C中,您可能已经多次调用 free (3),并完成了它。

因此,调用堆栈深度本身并不是一个问题。但IMO你的直觉是正确的:一个大的调用堆栈超过少量的功能表明一种超级组织的意大利面条代码。重新审视功能肯定会受到伤害,并且可能寻找减少抽象数量的方法。

答案 1 :(得分:0)

作为一项规则,是的,当我看到深调用堆栈时,我会产生怀疑,原因与 James K. Lowden 类似。

但是您的示例是规则的例外。看来您的代码正在遍历源代码的表示。源代码包含深度嵌套的结构,您的代码正在递归遍历这些结构。因此,调用堆栈将随着代码嵌套的深度而增长。这完全符合预期并且很好,尽管我希望您为堆栈分配了大量内存。