Исполняемый файл работает быстрее в Wine, чем в Windows - почему? - PullRequest
19 голосов
/ 10 ноября 2011

Решение: Очевидно, виновником было использование floor(), производительность которого в glibc зависит от ОС.


Это вопрос к предыдущему: Та же программа в Linux быстрее, чем в Windows - почему?

У меня есть небольшая программа на C ++, которая при компиляции с nuwen gcc 4.6.1 работает намного быстрее в Wine, чем в Windows XP (на том же компьютере). Вопрос: почему это происходит?

Время составляет ~ 15,8 и 25,9 секунды для Wine и Windows соответственно. Обратите внимание, что я имею в виду тот же исполняемый файл , а не только одну и ту же программу на C ++.

Исходный код находится в конце поста. Скомпилированный исполняемый файл здесь (если вы мне достаточно доверяете).

Эта конкретная программа не делает ничего полезного, это всего лишь минимальный пример, созданный мной из более крупной программы. Пожалуйста, посмотрите этот другой вопрос , чтобы узнать более точный сравнительный анализ исходной программы (важно !!) и исключить наиболее распространенные возможности (такие как другие программы, загружающие ЦП в Windows, штраф за запуск процесса, разницу в системе). вызовы, такие как выделение памяти). Также обратите внимание, что в то время как здесь я использовал rand() для простоты, в оригинале я использовал свой собственный RNG, который, как я знаю, не выделяет кучу.

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

Код:

#include <cstdlib>
#include <cmath>


int irand(int top) {
    return int(std::floor((std::rand() / (RAND_MAX + 1.0)) * top));
}

template<typename T>
class Vector {
    T *vec;
    const int sz;

public:
    Vector(int n) : sz(n) {
        vec = new T[sz];
    }

    ~Vector() {
        delete [] vec;
    }

    int size() const { return sz; }

    const T & operator [] (int i) const { return vec[i]; }
    T & operator [] (int i) { return vec[i]; }
};


int main() {
    const int tmax = 20000; // increase this to make it run longer
    const int m = 10000;
    Vector<int> vec(150);

    for (int i=0; i < vec.size(); ++i)
        vec[i] = 0;

    // main loop
    for (int t=0; t < tmax; ++t)
        for (int j=0; j < m; ++j) {
            int s = irand(100) + 1;
            vec[s] += 1;
        }

    return 0;
}

UPDATE

Похоже, что если я заменю irand() выше чем-то детерминированным, таким как

int irand(int top) {
    static int c = 0;
    return (c++) % top;
}

тогда разница во времени исчезнет. Хотелось бы отметить, что в моей исходной программе я использовал другой ГСЧ, а не систему rand(). Сейчас я копаюсь в источнике этого.

ОБНОВЛЕНИЕ 2

Теперь я заменил функцию irand() эквивалентом того, что имел в исходной программе. Он немного длинен (алгоритм взят из Числовые рецепты ), но цель состояла в том, чтобы показать, что никакие системные библиотеки не вызываются явно (кроме как через floor()). Тем не менее разница во времени все еще есть!

Может быть, floor() может быть виноват? Или компилятор генерирует вызовы к чему-то еще?

class ran1 {
    static const int table_len = 32;
    static const int int_max = (1u << 31) - 1;

    int idum;
    int next;
    int *shuffle_table;

    void propagate() {
        const int int_quo = 1277731;

        int k = idum/int_quo;
        idum = 16807*(idum - k*int_quo) - 2836*k;
        if (idum < 0)
            idum += int_max;
    }

public:
    ran1() {
        shuffle_table = new int[table_len];
        seedrand(54321);
    }
    ~ran1() {
        delete [] shuffle_table;
    }

    void seedrand(int seed) {
        idum = seed;
        for (int i = table_len-1; i >= 0; i--) {
            propagate();
            shuffle_table[i] = idum;
        }
        next = idum;
    }

    double frand() {
        int i = next/(1 + (int_max-1)/table_len);
        next = shuffle_table[i];
        propagate();
        shuffle_table[i] = idum;
        return next/(int_max + 1.0);
    }
} rng;


int irand(int top) {
    return int(std::floor(rng.frand() * top));
}

Ответы [ 4 ]

7 голосов
/ 10 ноября 2011

edit: Оказалось, что виновником было floor(), а не rand(), как я и подозревал - см. Обновление вверху вопроса ОП.

Время выполненияв вашей программе преобладают звонки на rand().

Поэтому я считаю, что rand() является виновником.Я подозреваю, что базовая функция обеспечивается средой выполнения WINE / Windows, и эти две реализации имеют разные характеристики производительности.

Самый простой способ проверить эту гипотезу - просто вызвать rand() в цикле, иодин и тот же исполняемый файл в обеих средах.

edit Я взглянул на исходный код WINE, и вот его реализация rand():

/*********************************************************************
 *              rand (MSVCRT.@)
 */
int CDECL MSVCRT_rand(void)
{
    thread_data_t *data = msvcrt_get_thread_data();

    /* this is the algorithm used by MSVC, according to
     * http://en.wikipedia.org/wiki/List_of_pseudorandom_number_generators */
    data->random_seed = data->random_seed * 214013 + 2531011;
    return (data->random_seed >> 16) & MSVCRT_RAND_MAX;
}

У меня нет доступа к исходному коду Microsoft для сравнения, но меня не удивит, если разница в производительности будет в получении локальных данных потока, а не в самом ГСЧ.

2 голосов
/ 10 ноября 2011

Из того, что я могу сказать, используемые стандартные библиотеки C БУДУТ отличаться в двух разных сценариях. Это влияет на вызов rand () и floor ().

С сайта mingw ... MinGW compilers provide access to the functionality of the Microsoft C runtime and some language-specific runtimes. Работая под XP, будут использоваться библиотеки Microsoft. Кажется, просто.

Однако модель под вином гораздо сложнее. Согласно этой диаграмме , в игру вступает libc операционной системы. Это может быть разницей между ними.

2 голосов
/ 10 ноября 2011

Википедия говорит:

Wine - это слой совместимости, а не эмулятор.Он дублирует функции компьютера Windows, предоставляя альтернативные реализации библиотек DLL, которые вызываются программами Windows, [необходима цитата] и процесс замены ядра Windows NT.Этот метод дублирования отличается от других методов, которые также могут рассматриваться как эмуляция, когда программы Windows запускаются на виртуальной машине. [2]Wine преимущественно пишется с использованием реверс-инжиниринга черного ящика, чтобы избежать проблем с авторским правом.

Это означает, что разработчики wine могут заменить вызов api на что-либо вообще до конечного результатабыло то же самое, что и при вызове Windows.И я полагаю, что они не были ограничены необходимостью сделать его совместимым с остальной частью Windows.

0 голосов
/ 10 ноября 2011

Хотя Wine в основном Windows, вы все равно сравниваете яблоки с апельсинами.Кроме того, это не только яблоки / апельсины, основные транспортные средства, перевозящие эти яблоки и апельсины вокруг, совершенно разные.

Короче говоря, ваш вопрос можно было бы перефразировать следующим образом: «Этот код работает на Mac OSX быстрее, чемделает на Windows "и получить тот же ответ.

...