Подтвердить что ты не робот

Executable работает быстрее на Wine, чем Windows - почему?

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


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

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

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

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

Эта конкретная программа не делает ничего полезного, это всего лишь минимальный пример, сложенный с большей программы, которую я имею. См. этот другой вопрос для более точного бенчмаркинга исходной программы (важно!!), и наиболее распространенные возможности исключены (например, другие программы, запугивающие CPU на 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;
}

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

ОБНОВЛЕНИЕ 2

Теперь я заменил функцию irand() эквивалентом того, что у меня было в исходной программе. Он немного длинный (алгоритм от Numerical Recipes), но дело в том, чтобы показать, что никакие системные библиотеки не называются эксплицитно (кроме возможно, через 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));
}
4b9b3361

Ответ 1

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

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

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

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

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

/*********************************************************************
 *              rand ([email protected])
 */
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 для сравнения, но меня это не удивило бы, если бы разница в производительности заключалась в получении потока-локального данных, а не в самой RNG.

Ответ 2

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

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

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

Ответ 3

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

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

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

Ответ 4

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

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