Почему динамическое связывание занимает так много времени? - PullRequest
0 голосов
/ 13 февраля 2019

Итак, я написал очень простую виртуальную машину, которая работает на небольшом подмножестве c-.Я профилировал его, чтобы попытаться увидеть узкие места, и результаты действительно смутили меня.73% времени использовалось в функции dl_relocate_object.В рамках этой функции 85% было потрачено на _dl_lookup_symbol_x.

Я не знаю много о внутренностях динамических библиотек, но я чувствую, что что-то не так.Основываясь на небольшом поиске, это означает, что в 75% случаев моя программа выполняет поиск функции в динамических библиотеках.Это звучит абсурдно для меня.

Когда я статически связываю двоичный файл, происходит увеличение скорости более чем в 2 раза, и худшая функция становится моей функцией VM :: run на 90%.Из этой функции 75% расходуется в ifstream.

По сути, мне интересно, есть ли у кого-нибудь представление о том, почему это происходит или это нормально.Когда я динамически связываю, моя программа работает примерно с той же скоростью, что и интерпретируемая версия программы, которая должна анализировать и анализировать необработанный текст.

Вот мой код:

#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();
}

Сторонаобратите внимание, я попытался загрузить весь файл в char [] во время инициализации виртуальной машины и затем использовать char [] вместо ifstream во время выполнения.Я также попытался использовать int [] для стека.Ни одно из этих изменений не имело никакого значения.

1 Ответ

0 голосов
/ 13 февраля 2019

Благодаря некоторым комментариям выше и этой ссылке Является ли DLL медленнее, чем статическая ссылка? Я решил провести тестирование с гораздо большим вводом и получил ожидаемые результаты.Очевидно, итерации цикла настолько быстры, что даже при 1_000 итерациях динамическое связывание все еще занимало большую часть времени.С 1_000_000 итераций код был сформирован, как и ожидалось.И статический, и динамический работают в очень похожие времена.Кроме того, при просмотре инструкций 1_000_000 он выполняется примерно в 9 раз быстрее, чем интерпретатор.

Примечание: у меня изначально были инструкции после цикла while, и поэтому у меня было goto.Я позже удалил их.Я думаю, это устарело.

...