Как выделить из кучи правильное выравнивание памяти для функции InterlockedIncrement? - PullRequest
3 голосов
/ 01 декабря 2010

Этот код работает, но правильно ли я использовал функцию InterlockedIncrement?Правильное выравнивание памяти m_count является моей главной задачей.Предположим, мы находимся в системе x86-64 и скомпилируем 64-битное приложение (в случае, если это имеет значение).Кстати, для моих реальных целей я не могу объявить m_count как volatile long, а затем использовать InterlockedIncrement (& m_count);но это должен быть указатель на данные в куче.

#include <Windows.h>
#include <malloc.h>

class ThreadSafeCounter {
public:
    ThreadSafeCounter()
    {
        // Are those arguments for size and alignment correct?
        void* placement = _aligned_malloc( sizeof(long), sizeof(long) );
        m_count = new (placement) long(0);
    }
    ~ThreadSafeCounter()
    {
        _aligned_free( const_cast<long*>(m_count) );
    }

    void AddOne()
    {
        InterlockedIncrement(m_count);
    }

    long GetCount()
    {
        return *m_count;
    }

private:
    volatile long* m_count;
};

Ответы [ 3 ]

5 голосов
/ 01 декабря 2010

Распределитель кучи уже выравнивает возвращаемые адреса по размеру слова собственной платформы. 4 байта для x86, 8 байтов для x64. Вы используете long , 32-битный на любой платформе для MSVC. Не нужно прыгать через обруч _aligned_malloc ().

3 голосов
/ 01 декабря 2010

Это деталь архитектуры платформы, но вы должны иметь в виду, что атомарные операции - это нечто большее, чем выравнивание.ABI платформы обычно по умолчанию обеспечивают выравнивание примитивного типа данных, чтобы любая операция (включая атомарные) работала.Функция malloc () никогда не должна возвращать вам неверно выровненный указатель, даже если вы запрашиваете один байт.

Хотя, в дополнение к этому, особенно следите за http://en.wikipedia.org/wiki/False_sharing - что означает необходимостьиметь выравнивание (обычно sizeof(long)), вы также должны убедиться, что размещаете только одну переменную с атомарным доступом в одной и той же кэшированной строке.

Это особенно важно, если вы планируете использовать / разрешать массивы этих счетчиков.

Компиляторы Microsoft используют __declspec(align(value)) для указания компилятору гарантировать выравнивание определенной структуры.Как уже упоминалось, похоже, что такая структура / класс данных не нуждается в распределении кучи, но я не знаю, нужен ли вам pimpl для чего-то другого.

1 голос
/ 01 декабря 2010

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

Однако, если вы в отчаянии, просто ознакомьтесь с реализацией shared_ptr в MSVC.

    typename aligned_storage<sizeof(_Ty),
        alignment_of<_Ty>::value>::type _Storage;
    };
    _Ty *_Getptr() const {  // get pointer
        return ((_Ty *)&_Storage);
    }

Этот C-cast довольно противный. Однако это наводит меня на мысль, что этот объект будет определенно иметь правильное выравнивание, используя черты типа.

...