Почему производительность CRITICAL_SECTION стала хуже на Win8 - PullRequest
0 голосов
/ 04 сентября 2018

Похоже, что производительность CRITICAL_SECTION стала хуже в Windows 8 и выше. (см. графики ниже)

Тест довольно прост: некоторые параллельные потоки делают по 3 миллиона блокировок каждый для доступа исключительно к переменной. Вы можете найти программу на C ++ внизу вопроса. Я запускаю тест на Windows Vista, Windows 7, Windows 8, Windows 10 (x64, VMWare, Intel Core i7-2600 3,40 ГГц).

Результаты на картинке ниже. Ось X - количество одновременных потоков. Ось Y - это истекшее время в секундах (чем ниже, тем лучше).

Test results

Что мы можем видеть:

  • SRWLock производительность примерно одинакова для всех платформ
  • CriticalSection производительность стала хуже относительно SRWL в Windows 8 и выше

Вопрос : Кто-нибудь может объяснить, почему производительность CRITICAL_SECTION стала хуже на Win8 и выше?


Некоторые заметки:

  • Результаты на реальных машинах практически одинаковы - CS гораздо хуже, чем std :: mutex, std :: recursive_mutex и SRWL на Win8 и выше. Однако у меня нет шансов запустить тест на разных ОС с одним и тем же процессором.
  • std::mutex Реализация для Windows Vista основана на CRITICAL_SECTION, но для Win7 и выше std::mutex основана на SWRL. Это верно как для MSVS17, так и для 15 (чтобы убедиться, что поиск файла primitives.h при установке MSVC ++ и поиск классов stl_critical_section_vista и stl_critical_section_win7) Это объясняет разницу между производительностью std :: mutex в Win Vista и другими.
  • Как сказано в комментариях, std::mutex является оболочкой, поэтому возможное объяснение некоторых издержек относительно SRWL может быть связано с накладными расходами, введенными кодом оболочки.

#include <chrono>
#include <iostream>
#include <mutex>
#include <string>
#include <thread>
#include <vector>

#include <Windows.h>

const size_t T = 10;
const size_t N = 3000000;
volatile uint64_t var = 0;

const std::string sep = ";";

namespace WinApi
{
    class CriticalSection
    {
        CRITICAL_SECTION cs;
    public:
        CriticalSection() { InitializeCriticalSection(&cs); }
        ~CriticalSection() { DeleteCriticalSection(&cs); }
        void lock() { EnterCriticalSection(&cs); }
        void unlock() { LeaveCriticalSection(&cs); }
    };

    class SRWLock
    {
        SRWLOCK srw;
    public:
        SRWLock() { InitializeSRWLock(&srw); }
        void lock() { AcquireSRWLockExclusive(&srw); }
        void unlock() { ReleaseSRWLockExclusive(&srw); }
    };
}

template <class M>
void doLock(void *param)
{
    M &m = *static_cast<M*>(param);
    for (size_t n = 0; n < N; ++n)
    {
        m.lock();
        var += std::rand();
        m.unlock();
    }
}

template <class M>
void runTest(size_t threadCount)
{
    M m;
    std::vector<std::thread> thrs(threadCount);

    const auto start = std::chrono::system_clock::now();

    for (auto &t : thrs) t = std::thread(doLock<M>, &m);
    for (auto &t : thrs) t.join();

    const auto end = std::chrono::system_clock::now();

    const std::chrono::duration<double> diff = end - start;
    std::cout << diff.count() << sep;
}

template <class ...Args>
void runTests(size_t threadMax)
{
    {
        int dummy[] = { (std::cout << typeid(Args).name() << sep, 0)... };
        (void)dummy;
    }
    std::cout << std::endl;

    for (size_t n = 1; n <= threadMax; ++n)
    {
        {
            int dummy[] = { (runTest<Args>(n), 0)... };
            (void)dummy;
        }
        std::cout << std::endl;
    }
}

int main()
{
    std::srand(time(NULL));
    runTests<std::mutex, WinApi::CriticalSection, WinApi::SRWLock>(T);
    return 0;
}

Тестовый проект был построен как консольное приложение Windows в Microsoft Visual Studio 17 (15.8.2) со следующими настройками:

  • Использование MFC: использование MFC в статической библиотеке
  • Windows SDK Версия: 10.0.17134.0
  • Набор инструментов платформы: Visual Studio 2017 (v141)
  • Оптимизация: O2, Oi, Oy-, GL

1 Ответ

0 голосов
/ 01 марта 2019

См. Критический раздел Windows - как полностью отключить вращение Начиная с Windows 8, Microsoft изменила реализацию (даже без слов в документации) поведения по умолчанию для критического раздела (если вы используете InitializeCriticalSection (& cs), вы получите вращение с включенным недокументированным алгоритмом регулировки динамического вращения). Смотрите мой комментарий здесь: https://randomascii.wordpress.com/2012/06/05/in-praise-of-idleness/#comment-57420

Для вашего теста попробуйте использовать InitializeCriticalSectionAndSpinCount (& cs, 1) вместо InitializeCriticalSection (& cs). Это должно сделать его похожим на Windows 7, хотя в этой области есть много других изменений.

...