首页 > 解决方案 > 为什么动态链接要花这么多时间?

问题描述

所以我写了一个非常非常基本的虚拟机,它运行在 c- 的一个小子集上。我正在对其进行分析以尝试查看瓶颈是什么,结果真的让我感到困惑。dl_relocate_object 函数中使用了 73% 的时间。在该函数中,85% 用于 _dl_lookup_symbol_x。

我对动态库的内部了解不多,但我觉得有些不对劲。基于一点点搜索,这意味着 75% 的时间,我的程序正在通过动态库搜索函数。这对我来说听起来很荒谬。

当我静态链接二进制文件时,速度提高了 2 倍以上,最差的功能变成了我的 VM::run 功能,达到 90%。在该功能中,75% 用于 ifstream。

基本上我想知道是否有人知道为什么会发生这种情况,或者这是否正常。当我动态链接时,我的程序的运行速度与必须对原始文本进行 lex 和解析的程序的解释版本大致相同。

这是我的代码:

#include <iostream>
#include <vector>
#include <fstream>

using namespace std;

enum opcodes{halt, loadInt, storeVar, loadVar, readVar, writeInt, writeString,
add, sub, mul, divide, eq, neq, leq, ls, gr, geq, notVal, andVal, orVal};

class VM {
    unsigned long pc;
    vector<int> stack;
    ifstream imem;
    char buf[1024*64];
    int var[256];
  public:
    VM(char* file){
        imem.open(file);
        imem >> noskipws;
        imem.rdbuf()->pubsetbuf(buf, 1024*64);
    }
    void run(){
        int x, y;
        char c;
        char instruction;
        while(imem >> instruction){
            switch(instruction){
                case halt:
                    goto exit_loop;
                case writeString:
                    imem >> c;
                    while(c != 0){
                        cout << c;
                        imem >> c;
                    }
                    cout << endl;
                    break;
                case loadInt:
                    imem >> c;
                    x = (c << 24);
                    imem >> c;
                    x |= (c << 16);
                    imem >> c;
                    x |= (c << 8);
                    imem >> c;
                    x |= c;
                    stack.push_back(x);
                    break;
                case storeVar:
                    imem >> c;
                    var[(int)c] = stack.back();
                    stack.pop_back();
                    break;
                case loadVar:
                    imem >> c;
                    stack.push_back(var[(int)c]);
                    break;
                case readVar:
                    imem >> c;
                    cin >> var[(int)c];
                    break;
                case writeInt:
                    x = stack.back();
                    stack.pop_back();
                    cout << x << endl;
                    break;
                case add:
                    y = stack.back();
                    stack.pop_back();
                    x =stack.back();
                    stack.pop_back();
                    stack.push_back(x + y);
                    break;
                case sub:
                    y = stack.back();
                    stack.pop_back();
                    x =stack.back();
                    stack.pop_back();
                    stack.push_back(x - y);
                    break;
                case mul:
                    y = stack.back();
                    stack.pop_back();
                    x =stack.back();
                    stack.pop_back();
                    stack.push_back(x * y);
                    break;
                case divide:
                    y = stack.back();
                    stack.pop_back();
                    x =stack.back();
                    stack.pop_back();
                    stack.push_back(x / y);
                    break;
                case eq:
                    y = stack.back();
                    stack.pop_back();
                    x =stack.back();
                    stack.pop_back();
                    stack.push_back((int)(x == y));
                    break;
                case neq:
                    y = stack.back();
                    stack.pop_back();
                    x =stack.back();
                    stack.pop_back();
                    stack.push_back((int)(x != y));
                    break;
                case leq:
                    y = stack.back();
                    stack.pop_back();
                    x =stack.back();
                    stack.pop_back();
                    stack.push_back((int)(x <= y));
                    break;
                case ls:
                    y = stack.back();
                    stack.pop_back();
                    x =stack.back();
                    stack.pop_back();
                    stack.push_back((int)(x < y));
                    break;
                case gr:
                    y = stack.back();
                    stack.pop_back();
                    x =stack.back();
                    stack.pop_back();
                    stack.push_back((int)(x > y));
                    break;
                case geq:
                    y = stack.back();
                    stack.pop_back();
                    x =stack.back();
                    stack.pop_back();
                    stack.push_back((int)(x >= y));
                    break;
                case notVal:
                    x = stack.back();
                    stack.pop_back();
                    stack.push_back((int)(!x));
                    break;
                case andVal:
                    y = stack.back();
                    stack.pop_back();
                    x =stack.back();
                    stack.pop_back();
                    stack.push_back((int)(x && y));
                    break;
                case orVal:
                    y = stack.back();
                    stack.pop_back();
                    x =stack.back();
                    stack.pop_back();
                    stack.push_back((int)(x || y));
                    break;
                default:
                    cout << "Error: Unknown Instruction" << endl;
                    goto exit_loop;
            }
        }
        exit_loop: ;
    };
};

int main(int argc, char** argv) {
    if(argc <= 1){
        cout << "Bad input" << endl;
    }
    VM vm(argv[1]);
    vm.run();
}

旁注,我尝试在 VM 初始化期间将整个文件加载到 char[] 中,然后在运行期间使用 char[] 而不是 ifstream。我也尝试过使用 int[] 作为堆栈。这些变化都没有任何区别。

标签: c++linuxperformancestatic-linkingdynamic-linking

解决方案


感谢上面的一些评论和这个链接DLL 比静态链接慢吗?我决定用更大的输入进行测试并得到预期的结果。显然循环迭代是如此之快,以至于即使有 1_000 次迭代,动态链接仍然需要大部分时间。通过 1_000_000 次迭代,代码按预期执行。静态和动态都在非常相似的时间运行。此外,在查看 1_000_000 指令时,它的运行速度比解释器快 9 倍。

旁注,我最初在 while 循环之后有说明,这就是我有 goto 的原因。我后来删除了它们。我想它现在已经过时了。


推荐阅读