GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 20.01.08 16:33
Оценка:
Как получить функциональность GetCurrentProcessorNumber()/NtGetCurrentProcessorNumber() под WinXP/2k?

Поиск дал следующие результаты.
1.
__declspec(naked) unsigned get_current_proc()
{
    __asm
    {
        mov ecx, 03Bh
        lsl eax, ecx
        shr eax, 0Eh
        retn
    }
}

Но как я понял, это будет работать только на тех версиях Windows, где работает и GetCurrentProcessorNumber(). Т.е. не будет работать под WinXP/2k.

2. С помощью инструкции CPUID получать номер APIC ID, по статической таблице переводить его в номер процессора. Это будет работать на всех версиях Windows. Практически то, что нужно, мне даже переводить в номер процессора не нужно, и так устроит, главное иметь уникальное число для каждого процессора.
Но тут полностью не устраивает, что на моём компьютере CPUID с EAX=1 выполняется 284 такта (полностью сериализует конвеер и выполняет полный барьер памяти).


В идеале хотелось чего-то вроде получения идентификатора текущего потока из блока информации потока.


з.ы. Не уверен, куда это больше — к WinAPI или Низкоуровнему программированию, поэтому пока закросспостю в оба форума. Если что можно из одного удалить.



1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: GetCurrentProcessorNumber() под WinXP
От: IID Россия  
Дата: 20.01.08 18:46
Оценка:
Здравствуйте, remark, Вы писали:

а если сразу после получение номера процессора поток будет перепланирован на другой процессор ?
kalsarikännit
Re[2]: GetCurrentProcessorNumber() под WinXP
От: Unmanaged Россия ICQ 476611995
Дата: 20.01.08 18:51
Оценка: +1
IID>а если сразу после получение номера процессора поток будет перепланирован на другой процессор ?

И что?
STATUS_INVALID_DEVICE_REQUEST
Re[2]: GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 20.01.08 18:54
Оценка:
Здравствуйте, IID, Вы писали:

IID>Здравствуйте, remark, Вы писали:


IID>а если сразу после получение номера процессора поток будет перепланирован на другой процессор ?


Могу предположить, что тогда поток будет выполняться на другом процессоре.


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: GetCurrentProcessorNumber() под WinXP
От: IID Россия  
Дата: 20.01.08 19:18
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, IID, Вы писали:


IID>>Здравствуйте, remark, Вы писали:


IID>>а если сразу после получение номера процессора поток будет перепланирован на другой процессор ?


R>Могу предположить, что тогда поток будет выполняться на другом процессоре.


R>


а какой тогда смысл в полученном номере ?
kalsarikännit
Re[4]: GetCurrentProcessorNumber() под WinXP
От: Unmanaged Россия ICQ 476611995
Дата: 20.01.08 19:44
Оценка:
IID>а какой тогда смысл в полученном номере ?

Смысл банальный — что в момент получения номера номер был правильный.
Это именно то, что требуется узнать, а именно — "номер в момент получения", а не "номер миллисекундой позже", — чувствуете разницу?
STATUS_INVALID_DEVICE_REQUEST
Re[4]: GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 20.01.08 19:54
Оценка:
Здравствуйте, IID, Вы писали:

IID>Здравствуйте, remark, Вы писали:


R>>Здравствуйте, IID, Вы писали:


IID>>>Здравствуйте, remark, Вы писали:


IID>>>а если сразу после получение номера процессора поток будет перепланирован на другой процессор ?


R>>Могу предположить, что тогда поток будет выполняться на другом процессоре.


IID>а какой тогда смысл в полученном номере ?


А какой смысл был бы в противном случае?


Если без шуток и это действительно интересно, то это значение можно охарактерировать как "хинт текущего процессора правильный в большинстве случаев и некоторый промежуток времени после выполнения функции". И именно с этой стороны его можно и нужно использовать. В реальности этот хинт обычно используется для контроля работы примитивов синхронизации, либо для оптимизации примитивов синхронизации.
За то, что он всё же нужен на практике говорит тот факт, что эту функция добавлена в WinAPI и не так давно в Linux.


R>>


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[5]: GetCurrentProcessorNumber() под WinXP
От: Аноним  
Дата: 20.01.08 20:53
Оценка:
U>Это именно то, что требуется узнать, а именно — "номер в момент получения", а не "номер миллисекундой позже", — чувствуете разницу?
а момент получения — это когда? до вызова call, после call? точно посередке вызывааемой функции? перед ret? после ret?
Re[5]: GetCurrentProcessorNumber() под WinXP
От: IID Россия  
Дата: 20.01.08 21:00
Оценка:
Здравствуйте, remark, Вы писали:


R>Если без шуток и это действительно интересно, то это значение можно охарактерировать как "хинт текущего процессора правильный в большинстве случаев и некоторый промежуток времени после выполнения функции".


Если только как отправную точку для сбора некоторой статистики, для которой не нужна абсолютная точность (см. выделенное), то да... Но опираться на полученное число, например, для организации логики какого-то своего примитива синхронизации, ИМХО неверно. Одно переключение между получением и использованием — и приплыли.
kalsarikännit
Re[6]: GetCurrentProcessorNumber() под WinXP
От: Unmanaged Россия ICQ 476611995
Дата: 20.01.08 21:09
Оценка: 1 (1)
U>>Это именно то, что требуется узнать, а именно — "номер в момент получения", а не "номер миллисекундой позже", — чувствуете разницу?
А>а момент получения — это когда? до вызова call, после call? точно посередке вызывааемой функции? перед ret? после ret?

А это как вам больше нравится .
STATUS_INVALID_DEVICE_REQUEST
Re[6]: GetCurrentProcessorNumber() под WinXP
От: Pzz Россия https://github.com/alexpevzner
Дата: 20.01.08 21:24
Оценка: 1 (1)
Здравствуйте, IID, Вы писали:

IID>Если только как отправную точку для сбора некоторой статистики, для которой не нужна абсолютная точность (см. выделенное), то да... Но опираться на полученное число, например, для организации логики какого-то своего примитива синхронизации, ИМХО неверно. Одно переключение между получением и использованием — и приплыли.


Для обеспечения корректности опираться на аккуратность номера текущего процессора неправильно, а для, например, оптимизации, вполне можно.
Re[7]: GetCurrentProcessorNumber() под WinXP
От: Аноним  
Дата: 20.01.08 22:56
Оценка: +1 -2
U>А это как вам больше нравится .
А мне никак не нравится. Такой вызов имеет смысл только на IRQL>=DISPATCH_LEVEL остальное — быдлокодерство, основанное на женской логике.
Re[8]: GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 20.01.08 23:20
Оценка: -2
Здравствуйте, Аноним, Вы писали:

U>>А это как вам больше нравится .

А>А мне никак не нравится. Такой вызов имеет смысл только на IRQL>=DISPATCH_LEVEL остальное — быдлокодерство, основанное на женской логике.

Сразу видно — человек не занимался эффективной синхронизацией


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[8]: GetCurrentProcessorNumber() под WinXP
От: Unmanaged Россия ICQ 476611995
Дата: 20.01.08 23:33
Оценка:
А>...остальное — быдлокодерство, основанное на женской логике.

Интересная фраза, надо запомнить .
STATUS_INVALID_DEVICE_REQUEST
Re[9]: GetCurrentProcessorNumber() под WinXP
От: Andrew.W Worobow https://github.com/Worobow
Дата: 21.01.08 08:12
Оценка: 1 (1) +1
Здравствуйте, remark, Вы писали:


R>Сразу видно — человек не занимался эффективной синхронизацией


IDD прав, и прав на 100%. Не занимайтесь фигней, только если это не ваше хобби. Синхронизации в пользовательском режиме не строят на знании номера текущего процессора. Для общности можете всегда его считать равным одному. Сделайте через дефайн....
Не все кто уехал, предал Россию.
Re[10]: GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 21.01.08 17:41
Оценка:
Здравствуйте, Andrew.W Worobow, Вы писали:

AWW>Здравствуйте, remark, Вы писали:


R>>Сразу видно — человек не занимался эффективной синхронизацией


AWW>IDD прав, и прав на 100%.


Могу только сказать, что IDD и те, кто его поддерживают, не разбираются в эффективной синхронизации.
Я, в принципе, не удивлён такому повороту событий, т.к. в каждой ветке, где идёт обсуждение этого вопроса появляются несколько человек, которые начинают говорить, "да это не работает", "да уже в следующем такте", "да это бесполезно", "вам нужен ид потока", "не занимайтесь быдлокодерством":
http://groups.google.com/group/comp.os.linux.development.system/browse_frm/thread/724a00185ad9b82a
http://groups.google.com/group/comp.os.ms-windows.programmer.win32/browse_frm/thread/c428be6003b793a8
http://groups.google.com/group/comp.os.ms-windows.programmer.nt.kernel-mode/browse_frm/thread/e325e216da2ce409
http://groups.google.com/group/microsoft.public.win32.programmer.kernel/browse_frm/thread/20ce7838fcef4606
http://groups.google.com/group/microsoft.public.win32.programmer.kernel/browse_frm/thread/663176629a3ff210
http://gzip.rsdn.ru/forum/message/2804333.aspx
Автор: remark
Дата: 20.01.08

http://gzip.rsdn.ru/forum/message/2804334.aspx
Автор: remark
Дата: 20.01.08


Почему все как огня бояться этой вещи


AWW> Синхронизации в пользовательском режиме не строят на знании номера текущего процессора.


Это аксиома или мантра?


AWW> Для общности можете всегда его считать равным одному. Сделайте через дефайн....


Как back-off, видимо, придётся это использовать на тех платформах, где не соотв. функции.



1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[11]: GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 21.01.08 17:42
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, Andrew.W Worobow, Вы писали:


AWW>>Здравствуйте, remark, Вы писали:


R>>>Сразу видно — человек не занимался эффективной синхронизацией


AWW>>IDD прав, и прав на 100%.


R>Могу только сказать, что IDD и те, кто его поддерживают, не разбираются в эффективной синхронизации.

R>Почему все как огня бояться этой вещи


Постараюсь в ближайшее время показать реальный пример использования этой функции.


R>


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[11]: GetCurrentProcessorNumber() под WinXP
От: Andrew.W Worobow https://github.com/Worobow
Дата: 21.01.08 18:15
Оценка: +1
Здравствуйте, remark, Вы писали:

R>>>Сразу видно — человек не занимался эффективной синхронизацией

AWW>>IDD прав, и прав на 100%.
R>Могу только сказать, что IDD и те, кто его поддерживают, не разбираются в эффективной синхронизации.

Да что ты говоришь. Пока что я вижу, что к сожалению у тебя имеются некоторые пробелы в понимании многозадачности. И видимо отсюда и стремление получить то что ненужно.

Попробуй просто описать идею, тогда проще будет тебе обьяснить твою ошибки.

R>Почему все как огня бояться этой вещи


Да никто не боится, просто возможно модель у тебя в голове не соответстует реалиям. Вполне возможно и отсюда стемление получить то что и так есть например.

AWW>> Синхронизации в пользовательском режиме не строят на знании номера текущего процессора.


R>Это аксиома или мантра?


Это просто очевидно. На 100%. Либо афинити маска, либо ничего.
Не все кто уехал, предал Россию.
Re[9]: GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 21.01.08 22:38
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, Аноним, Вы писали:


U>>>А это как вам больше нравится .

А>>А мне никак не нравится. Такой вызов имеет смысл только на IRQL>=DISPATCH_LEVEL остальное — быдлокодерство, основанное на женской логике.

R>Сразу видно — человек не занимался эффективной синхронизацией


Смысл имеет и то, и другое. Только разный.

R>


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[12]: GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 22.01.08 01:03
Оценка: 99 (8)
Здравствуйте, Andrew.W Worobow, Вы писали:

R>>>>Сразу видно — человек не занимался эффективной синхронизацией


AWW>Да что ты говоришь. Пока что я вижу, что к сожалению у тебя имеются некоторые пробелы в понимании многозадачности. И видимо отсюда и стремление получить то что ненужно.


AWW>Попробуй просто описать идею, тогда проще будет тебе обьяснить твою ошибки.



Отвечаю сразу всем. А именно: Andrew.W Worobow, IID, Геннадий Майко.

Вначале вспомогательный вопрос — почему в WinAPI была добавлена "ненужная", "неправильная", "быдлокодерская" и так далее функция GetCurrentProcessorNumber()? И так же в Linux API аналогичная функция vgetcpu()? Сам факт добавления такой функции в API двух основных ОС наталкивает на мысли.

Относительно WinAPI мы скорее всего не увидим официальных причин добавления, а вот с Linux всё значительно прозрачнее — вот выдержка из сопроводительной записки к патчу с функцией vgetcpu():

I got several requests over the years to provide a fast way to get
the current CPU and node on x86-64. That is useful for a couple of things:

— The kernel gets a lot of benefit from using per CPU data to get better
cache locality and avoid cache line bouncing. This is currently
not quite possible for user programs. With a fast way to know the current
CPU user space can use per CPU data that is likely in cache already.
Locking is still needed of course — after all the thread might switch
to a different CPU — but at least the memory should be already in cache
and locking on cached memory is much cheaper.

— For NUMA optimization in user space you really need to know the current
node to find out where to allocate memory from.
If you allocate a fresh page from the kernel the kernel will give you
one in the current node, but if you keep your own pools like most programs
do you need to know this to select the right pool.
On single threaded programs it is usually not a big issue because they
tend to start on one node, allocate all their memory there and then eventually
use it there too, but on multithreaded programs where threads can
run on different nodes it's a bigger problem to make sure the threads
can get node local memory for best performance.

At first look such a call still looks like a bad idea — after all the kernel can
switch a process at any time to other CPUs so any result of this call might
be wrong as soon as it returns.

But at a closer look it really makes sense:
— The kernel has strong thread affinity and usually keeps a process on the
same CPU. So switching CPUs is rare. This makes it an useful optimization.

The alternative is usually to bind the process to a specific CPU — then it
"know" where it is — but the problem is that this is nasty to use and
requires user configuration. The kernel often can make better decisions on
where to schedule. And doing it automatically makes it just work.

This cannot be done effectively in user space because only the kernel
knows how to get this information from the CPUs because it requires
translating local APIC numbers to Linux CPU numbers.

Doing it in a syscall is too slow so doing it in a vsyscall makes sense.

...

My eventual hope is that glibc will be start using this to implement a NUMA aware
malloc() in user space that tries to allocate local memory preferably.
I would say that's the biggest gap we still have in "general purpose" NUMA tuning
on Linux. Of course it will be likely useful for a lot of other scalable
code too.

...

-Andi Kleen




Конкретный пример. Задача — поддерживать некоторый счётчик в многопоточной среде. Вот два варианта решения. Первый — в лоб — с помощью InterlockedIncrement() над одной переменной. Второй — распределённый — на основе GetCurrentProcessorNumber().

namespace arch
{
    unsigned const cacheline_size = 128;
    unsigned const processor_count = 4;
}

struct padded_value
{
    long volatile value;
    char pad [arch::cacheline_size - sizeof(long)];
};

padded_value values [arch::processor_count];

void thread_func1()
{
    for (int i = 0; i != 10000000; ++i)
    {
        if (i % 1000)
        {
            _InterlockedIncrement(&values[0].value);
        }
        else
        {
            volatile int x = values[0].value;
        }
    }
}

void thread_func2()
{
    for (int i = 0; i != 10000000; ++i)
    {
        if (i % 1000)
        {
            _InterlockedIncrement(&values[GetCurrentProcessorNumber()].value);
        }
        else
        {
            volatile int x = 0;
            for (int j = 0; j != arch::processor_count; ++j)
            {
                x += values[j].value;
            }
        }
    }
}



Я провёл бенчмарк обоих функций. Запускал 32 потока на машине c 4-ёх-ядерным процессором в 4-ёх вариациях:
1. все потоки привязаны к одному ядру, это фактически эмуляция одноядерного процессора
2. все потоки привязаны к ядрам 0 и 1, это фактически эмуляция двухядерного процессора
3. все потоки привязаны к ядрам 0 и 2, это фактически эмуляция двухпроцессорной машины, т.к. кэш L2 разделяется между ядрами 0 и 1, и 2 и 3 соотв.
4. без привязки

Вот результат для второго варианта с GetCurrentProcessorNumber():

кол-во тактов на итерациюмасштабирование
1261
2281.854
3281.785
4283.645
Ну что ж, масштабирование 3.645 на 4-ёх ядрах — это приемлимо. Теперь смотрим первый вариант, без использования GetCurrentProcessorNumber():

кол-во тактов на итерациюмасштабирование
1241
2760.6361
31200.4038
43760.2585
Я думаю, тут комментарии излишни.

Вот так с помощью стрёмной функции, иногда выдающей неправильные результаты можно изменить масштабирование примитива с "линейно убывающего" на "линейно возрастающее".

Теперь об альтернативах GetCurrentProcessorNumber(). Собственно видится 2 основные альтернативы. Первая — использовать жёсткую привязку потоков к процессорам, тогда всегда можно сказать, на каком процессоре выполняется код. Иногда это — решение. Но зачастую жёсткая привязка это не то, с чем хочется связываться. Andi Kleen охарактеризовал это так:

but the problem is that this is nasty to use and
requires user configuration. The kernel often can make better decisions on
where to schedule. And doing it automatically makes it just work

Ну и достаточно просто упомянуть библиотечный код, что бы стало понятно, что это *не* общее решение.

Вторая альтернатива — использовать везде идентификатор потока вместо идентификатора процессора. Зачастую это — *хорошее* решение. В конце концов идентификатор потока не меняется и можно рассчитывать на 100% точность. Но вот 3 причины, которые сразу приходят к голову, почему это *не* универсальное решение и почему идентификатор процессора может быть лучше в каких-то ситуациях:
— Потоков может быть значительно больше, чем процессоров. Процессоров обычно 1, 2, 4. Потоков — 10, 20, 40. Соотв. когда дело дойдёт до пуллинга или агрегации придётся платить за это количество.
— Кол-во процессоров не изменяется во время работы. Соотв. можно, например, при инициализации примитива сразу выделить блоков памяти по кол-ву процессоров. С потоками значительно проблематичнее — они могут создаваться и удаляться во время работы. Допустим нам надо иметь блок памяти на каждый поток. Мы оказываемся в достаточно затруднительном положении.
— От данных, связанных с потоком, основной смысл тогда, когда работа с ними ведётся через обычные операции, а не атомарные (Interlocked). Но если мы, например, строим примитив взаимного исключения, то с данными, связанными с потоком, всё равно придётся работать с помощью атомарных операций. А тогда и весь смысл теряется по сравнению с данными, связанными с процессором. Только хуже, т.к. потоков больше, чем процессоров.

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

Исходя из этого, использование номера текущего процессора может быть оправдано. Пример со счётчиком, конечно, немного надуман, но я сейчас строю на такой же основе масштабируемый распределенный reader-writer mutex. Идея, в принципе, достаточно тривиальна — я думаю, что аналогичные решения должны применяться в ядрах ОС. Такой мьютекс будет обладать линейной масштабируемостью на стороне читателя и допускать истинно параллельный доступ на чтение, в отличие от традиционных reader-writer mutex, которые во многих ситуациях не позволяют истинно параллельного доступа на чтение, обходятся дороже чем простой мьютекс и обладают отрицательной масштабируемостью ещё худшей, чем у обычного мьютекса.

Я надеюсь, что часть вопросов отпала. Осталось только понять как можно получить GetCurrentProcessorNumber() под WinXP


1024cores — all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[13]: GetCurrentProcessorNumber() под WinXP
От: Unmanaged Россия ICQ 476611995
Дата: 22.01.08 01:45
Оценка: 1 (1)
R>Отвечаю сразу всем...

Ай да молодец!
STATUS_INVALID_DEVICE_REQUEST
И тем не менее.
От: Блудов Павел Россия  
Дата: 22.01.08 03:15
Оценка: 6 (2)
Здравствуйте, remark!

У вашего примера есть один фатальный недостаток — счетчик меряет сам себя.
В итоге в случае с
_InterlockedIncrement(&values[0].value);

имеем забавную картину: всё процессоры непрерывно ломятся в один и тот же адрес пямяти.
По определению, они могут это делать только по очереди, иначе бы терялся весь смысл InterlockedIncrement'а.
Отсюда и масштабируемость 1/кол-во процессоров.

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

Резюмируя вынужден заключить, что если вы не разрабатываете сверхбыстрый менеджер памяти (вот для них и добавлен vgetcpu) или что-то навроде, то вы всё-таки занимаетесь быдлокодерством. Для поддержки некоторого счётчика в многопоточной среде таких наворотов не требуется.
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[13]: GetCurrentProcessorNumber() под WinXP
От: rus blood Россия  
Дата: 22.01.08 11:12
Оценка: 11 (1)
Здравствуйте, remark, Вы писали:

R>Отвечаю сразу всем. А именно: Andrew.W Worobow, IID, Геннадий Майко.


Посмотри здесь.
Имею скафандр — готов путешествовать!
Re: И тем не менее.
От: remark Россия http://www.1024cores.net/
Дата: 22.01.08 14:34
Оценка:
Здравствуйте, Блудов Павел, Вы писали:

БП>Здравствуйте, remark!


БП>У вашего примера есть один фатальный недостаток — счетчик меряет сам себя.

БП>В итоге в случае с
БП>
БП>_InterlockedIncrement(&values[0].value);
БП>

БП>имеем забавную картину: всё процессоры непрерывно ломятся в один и тот же адрес пямяти.
БП>По определению, они могут это делать только по очереди, иначе бы терялся весь смысл InterlockedIncrement'а.
БП>Отсюда и масштабируемость 1/кол-во процессоров.

Отсюда бы следовала масштабируемость 1, а не 1/N.


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

БП>Добавьте в ваш код хотябы вызов QueryPerformanceCounter и получите масштабируемость по количеству процессоров.

Хорошо, это разумно. Я добавлю некую локальную полезную работу тактов на 500 между инкрементами и приведу результаты.
В любом случае, добавление дополнительной работы не как не изменит *абсолютной* стоимости операций, оно изменит только *относительную* стоимость операций. Предсавь тебе на работе выдали ноутбук размером с холодильник и сказали ходить с ним, ты говоришь "да вы поглядите, он же огромный", а тебе предлагают "а ты погляди на него с крыши высотного здания. видишь, отсюда, он не такой уж и большой". Ты предлагаешь примерно то же самое.


БП>Резюмируя вынужден заключить, что если вы не разрабатываете сверхбыстрый менеджер памяти (вот для них и добавлен vgetcpu) или что-то навроде, то вы всё-таки занимаетесь быдлокодерством. Для поддержки некоторого счётчика в многопоточной среде таких наворотов не требуется.


Я бы не стал заявлять так категорично и безоговорочно. Я безусловно согласен, что во *многих* случаях этого не требуется. Но я же не заставляю всех применять такие техники, я просто споросил, не лежитли где в TIB/TEB/CONTEXT хинт текущего процессора. Свои случаи я в силах измерить вдоль и поперёк, что бы заключить нужно ли мне применять такие техники или нет.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[14]: GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 22.01.08 17:19
Оценка:
Здравствуйте, rus blood, Вы писали:

RB>Здравствуйте, remark, Вы писали:


R>>Отвечаю сразу всем. А именно: Andrew.W Worobow, IID, Геннадий Майко.


RB>Посмотри здесь.



Да, это именно то, что я описал в первом посте.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 22.01.08 19:07
Оценка: 6 (1)
Здравствуйте, remark, Вы писали:

R>2. С помощью инструкции CPUID получать номер APIC ID, по статической таблице переводить его в номер процессора. Это будет работать на всех версиях Windows. Практически то, что нужно, мне даже переводить в номер процессора не нужно, и так устроит, главное иметь уникальное число для каждого процессора.

R>Но тут полностью не устраивает, что на моём компьютере CPUID с EAX=1 выполняется 284 такта (полностью сериализует конвеер и выполняет полный барьер памяти).


У меня получился следующий набросок:

struct current_processor_t
{
    unsigned number;
    unsigned timestamp;
    unsigned period;
};

__declspec(thread) current_processor_t current_processor = {0, 0, 1};

__declspec(noinline) void update_current_processor()
{
    current_processor_t& cp = current_processor;
    cp.timestamp = *(unsigned*)0x7FFE0000; // tick count
    cp.period = 10; // subject to tweaking
    unsigned proc;
    __asm
    {
        mov eax, 1;
        cpuid;
        shr ebx, 24;
        mov proc, ebx;
    }
    cp.number = proc;
}

unsigned get_current_processor()
{
    current_processor_t& cp = current_processor;
    if (0 == --cp.period || cp.timestamp != *(unsigned*)0x7FFE0000)
        update_current_processor();
    return cp.number;
}



Идея следующая. Номер процессора получается с помощью cpuid. Но из-за дороговизны он кэшируется. Обновление кэша происходит при выполнении одного из двух условий. Первое — не реже, чем раз в N считываний. Второе — при изменении времени (того, что получается с помощью GetTickCount()).
N тут параметр для твикинга. У меня получается, при N = 5 функция работает в среднем 73 такта, при N = 10 — 40 тактов, при N = 20 — 24 такта.

Точность такого кэширования пока под вопросом. Скорее всего точность будет зависеть от нагрузки. Т.е. можно создать тест, где точность будет близка к 100%, а можно, где будет < 100%. Про "общий случай" пока сказать сложно. Но если эта функция вызывается часто, то точность должна быть приемлемой. Если же вызывается редко, то какая разница, какая у неё точность... я так думаю.


R>


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 22.01.08 19:08
Оценка:
Здравствуйте, remark, Вы писали:

R>Здравствуйте, remark, Вы писали:


R>>2. С помощью инструкции CPUID получать номер APIC ID, по статической таблице переводить его в номер процессора. Это будет работать на всех версиях Windows. Практически то, что нужно, мне даже переводить в номер процессора не нужно, и так устроит, главное иметь уникальное число для каждого процессора.

R>>Но тут полностью не устраивает, что на моём компьютере CPUID с EAX=1 выполняется 284 такта (полностью сериализует конвеер и выполняет полный барьер памяти).


R>У меня получился следующий набросок:


Возможно эта функция не будет различать разные аппаратные потоки на процессорах с HT. Но мне лично в принципе это и не нужно. Так даже может и лучше будет.


R>>

R>

1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: И тем не менее.
От: Блудов Павел Россия  
Дата: 23.01.08 01:29
Оценка: 11 (1)
Здравствуйте, remark, Вы писали:

R>я просто споросил, не лежитли где в TIB/TEB/CONTEXT хинт текущего процессора.

Нет не лежит. В том смысле, что его туда некому класть. Похоже что тут
Автор: remark
Дата: 20.01.08
ответ правильный.
Есть слабая надежда на SP3 для XP, но для Win2k надежды никакой.
ИМХО, особой беды тут нет — вероятность что на новой машине будет стоять win2k довольно мала, а старые в основном однопроцессорные и одноядерные.
Тем, кто будет жаловаться на недостаточную масштабируемость вашего проиложения можно смело советовать апгрейд.
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[3]: GetCurrentProcessorNumber() под WinXP
От: Блудов Павел Россия  
Дата: 23.01.08 01:36
Оценка: 11 (1)
Здравствуйте, remark, Вы писали:

R>Возможно эта функция не будет различать разные аппаратные потоки на процессорах с HT. Но мне лично в принципе это и не нужно. Так даже может и лучше будет.

Тесты показали что пример работает с ГН.
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[2]: И тем не менее.
От: rus blood Россия  
Дата: 23.01.08 07:55
Оценка: 11 (1)
Здравствуйте, remark, Вы писали:

БП>>имеем забавную картину: всё процессоры непрерывно ломятся в один и тот же адрес пямяти.

БП>>По определению, они могут это делать только по очереди, иначе бы терялся весь смысл InterlockedIncrement'а.
БП>>Отсюда и масштабируемость 1/кол-во процессоров.

R>Отсюда бы следовала масштабируемость 1, а не 1/N.


В твоем примере небольшой обман. В одном случае 32 потока работают с одной переменной. В другом — размазаны на 4-е. Дополнительный эффект торможения "достигается" из-за бОльшей "нагрузки" поток/переменная.

Неплохо было бы показать, что GetCurrentProcessNumber — лучший способ распределения потоков по переменным. Надо сравнить с другими вариантами распределения:
— жесткая равномерная привязка потоков к переменным
— случайное распределение (индексы можно нагенерировать заранее)
— что-то типа "(c + i) % processor_number", где c — свой для каждого потока.


Просто интересно...

ЗЫ
Сорри за cpuid — за разразившейся микровойной захотелось помочь, забыл проверить исходный пост...
Имею скафандр — готов путешествовать!
Re[3]: И тем не менее.
От: remark Россия http://www.1024cores.net/
Дата: 23.01.08 11:15
Оценка:
Здравствуйте, rus blood, Вы писали:

RB>Здравствуйте, remark, Вы писали:


БП>>>имеем забавную картину: всё процессоры непрерывно ломятся в один и тот же адрес пямяти.

БП>>>По определению, они могут это делать только по очереди, иначе бы терялся весь смысл InterlockedIncrement'а.
БП>>>Отсюда и масштабируемость 1/кол-во процессоров.

R>>Отсюда бы следовала масштабируемость 1, а не 1/N.


RB>В твоем примере небольшой обман. В одном случае 32 потока работают с одной переменной. В другом — размазаны на 4-е. Дополнительный эффект торможения "достигается" из-за бОльшей "нагрузки" поток/переменная.


Ну в принципе, согласен. Более честно было бы сравнивать работу с 4 переменными с помощью GetCurrentProcessNumber(), в другом с помощью какого-то другого распределения.


RB>Неплохо было бы показать, что GetCurrentProcessNumber — лучший способ распределения потоков по переменным. Надо сравнить с другими вариантами распределения:

RB>- жесткая равномерная привязка потоков к переменным
RB>- случайное распределение (индексы можно нагенерировать заранее)
RB>- что-то типа "(c + i) % processor_number", где c — свой для каждого потока.
RB>-


Попробую протестить — просто не всегда есть время на это.
Хотя я уверен, что результат будет в пользу GetCurrentProcessNumber(), хотя, конечно, распределение нагрузки на 4 переменных увеличит масштабируемость.



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[3]: И тем не менее.
От: remark Россия http://www.1024cores.net/
Дата: 23.01.08 11:40
Оценка:
Здравствуйте, Блудов Павел, Вы писали:

БП>Здравствуйте, remark, Вы писали:


R>>я просто споросил, не лежитли где в TIB/TEB/CONTEXT хинт текущего процессора.

БП>Нет не лежит. В том смысле, что его туда некому класть.

В смысле не лежит под XP или не лежит вообще?

Я себе представляю это так. Шедулеру после выбора потока на исполнение ничего не стоит записать вместе с локальными данным потока так же и номер процессора. Шедулер всегда знает номер процессора на который он направляет на исполнение, т.ч. это просто одна локальная запись — практически бесплатно. Я бы по крайней мере сделал бы так.
Не понимаю, почему они делают это таким извращенным способом через инструкцию lsl. Выглядит как затычка. Неужели они не могут расширять информацию хранимую, в [fs].


БП>Похоже что тут
Автор: remark
Дата: 20.01.08
ответ правильный.

БП>Есть слабая надежда на SP3 для XP, но для Win2k надежды никакой.
БП>ИМХО, особой беды тут нет — вероятность что на новой машине будет стоять win2k довольно мала, а старые в основном однопроцессорные и одноядерные.
БП>Тем, кто будет жаловаться на недостаточную масштабируемость вашего проиложения можно смело советовать апгрейд.

В принципе — да, но не приятно включать и описывать такие артефакты...
Тем более похоже, что амортизированный вариант с cpuid скорее всего будет работать. Похоже, что функция vgetcpu() работает примерно таким же образом. Хотя не понятно как они с частой jiffies 100Hz добились точности...



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[4]: И тем не менее.
От: Блудов Павел Россия  
Дата: 23.01.08 12:29
Оценка:
Здравствуйте, remark, Вы писали:

R>В смысле не лежит под XP или не лежит вообще?

Не лежит под XP. Шедулер от XP её не помещает в доступный из user mode участок памяти.
... << RSDN@Home 1.2.0 alpha rev. 786>>
Re[4]: И тем не менее.
От: Andrew.W Worobow https://github.com/Worobow
Дата: 23.01.08 13:04
Оценка:
Здравствуйте, remark, Вы писали:

R>Я себе представляю это так. Шедулеру после выбора потока на исполнение ничего не стоит записать вместе с локальными данным потока так же и номер процессора. Шедулер всегда знает номер процессора на который он направляет на исполнение, т.ч. это просто одна локальная запись — практически бесплатно. Я бы по крайней мере сделал бы так.

R>Не понимаю, почему они делают это таким извращенным способом через инструкцию lsl. Выглядит как затычка. Неужели они не могут расширять информацию хранимую, в [fs].

Если речь идет про WINDOWS, то кто такой шедуллер?
Не все кто уехал, предал Россию.
Re[13]: GetCurrentProcessorNumber() под WinXP
От: TarasCo  
Дата: 23.01.08 13:39
Оценка:
R>padded_value values [arch::processor_count];

а начальный адрес этого массива попадает на границу линейки кеша? Иначе результаты могут не совсем достоверные. Конечно, компилятор должен был бы постараться — но не факт . Если речь идет про VC, было бы наверное правильно в описании структуры padded_value добавить __declspec(align(128))
Да пребудет с тобою сила
Re: GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 31.01.08 16:48
Оценка:
Здравствуйте, remark, Вы писали:

R>Как получить функциональность GetCurrentProcessorNumber()/NtGetCurrentProcessorNumber() под WinXP/2k?



На AMD64 есть инструкция RDTSCP (Read Time-Stamp Counter and Processor ID), которая, помимо TS счётчика, возвращает так же идентификатор процессора.
Ложку дёгтя добавляет факт, что RDTSCP, в отличие от оригинальной RDTSC, сериализует выполнение конвейера, т.е. ждёт retirement'а всех предыдущих инструкций. Смысл в общем-то понятен — сделать более удобным и быстрым замер времени выполнения участков кода. Но блин, сделали бы возможность отдельно получать только Processor ID, и без сериализации, тогда бы и северный мост не нагружался.
А так вообще latecy: 41 core clocks + 16 Northbridge clocks.


R>


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: GetCurrentProcessorNumber() под WinXP
От: rus blood Россия  
Дата: 17.02.08 14:56
Оценка:
Здравствуйте, remark, Вы писали:

R>Идея следующая. Номер процессора получается с помощью cpuid. Но из-за дороговизны он кэшируется. Обновление кэша происходит при выполнении одного из двух условий. Первое — не реже, чем раз в N считываний. Второе — при изменении времени (того, что получается с помощью GetTickCount()).


Странные условия.
Ни то, ни другое не имеет отношения к перепланировке потоков.
Время в shared_user_data может как измениться во время исполнения потока, так может и не измениться после переключения с потока на другой и обратно.
Имею скафандр — готов путешествовать!
Re[3]: GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 18.02.08 10:00
Оценка:
Здравствуйте, rus blood, Вы писали:

RB>Здравствуйте, remark, Вы писали:


R>>Идея следующая. Номер процессора получается с помощью cpuid. Но из-за дороговизны он кэшируется. Обновление кэша происходит при выполнении одного из двух условий. Первое — не реже, чем раз в N считываний. Второе — при изменении времени (того, что получается с помощью GetTickCount()).


RB>Странные условия.

RB>Ни то, ни другое не имеет отношения к перепланировке потоков.
RB>Время в shared_user_data может как измениться во время исполнения потока, так может и не измениться после переключения с потока на другой и обратно.

Я с радостью готов выслушать любые предложения по улучшению алгоритма.
Какой вариант ты можешь предложить?


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[2]: GetCurrentProcessorNumber() под WinXP
От: Аноним  
Дата: 19.02.08 10:35
Оценка:
Здравствуйте, remark, Вы писали:

может это избавит от страданий?
Re[3]: GetCurrentProcessorNumber() под WinXP
От: remark Россия http://www.1024cores.net/
Дата: 19.02.08 10:38
Оценка: :)
Здравствуйте, Аноним, Вы писали:

А>Здравствуйте, remark, Вы писали:


А>может это избавит от страданий?


Это ничтожная библиотека. Максимум для высокопроизводительных вычислений.


1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re: попробуй Красную Пилюлю
От: gear nuke  
Дата: 27.02.08 06:04
Оценка: 30 (1)
Здравствуйте, remark,

Есть такая штука — http://invisiblethings.org/papers/redpill.html

Она впринципе не совсем справляется с заявленными целями, поскольку у каждого ядра своя IDT. Но для идентификации процессора по-моему подойти должна
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[2]: попробуй Красную Пилюлю
От: gear nuke  
Дата: 27.02.08 17:51
Оценка: 33 (2)
Без шелкодов:
#include <windows.h>
#include <conio.h>
#include <stdio.h>

unsigned get_idt_base()
{
#pragma pack(push, 1)
  struct { WORD limit; DWORD base; } idt;
#pragma pack(pop)
  __asm lea ecx, idt
  __asm sidt [ecx]
  return idt.base;
}

int main()
{
  for ( unsigned cpu = 0; SetThreadAffinityMask(GetCurrentThread(), 1 << cpu); ++cpu )
  {
    printf("CPU#%u IDT base is %x\n", cpu, get_idt_base());
  }
  _getch();
}


ЗЫ начиная с MSVC 2005 SP1 есть интринсик __sidt
People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
Re[3]: попробуй Красную Пилюлю
От: remark Россия http://www.1024cores.net/
Дата: 29.03.08 13:10
Оценка:
Здравствуйте, gear nuke, Вы писали:

Сорри за задержку — не было времени разобраться и проверить.
Выглядит очень круто!
На последних AMD, согласно документации выполняется 7 тактов. На Intel документации не нашёл, но на тех, на которых проверил, тоже порядка 7 тактов. Конвеер не сериализует.
Хммм... Странно, что я нигде не нашёл про неё. Вопрос неоднократно поднимался в USENET...

На многопроцессорных/многоядерных WinXP IDT действительно разная на каждый процессор. А есть какие-нибудь гарантии на этот счёт? Чем это обусловлено?
Ну хотя наврядли это уже измениться на Win32, Win2k. А на Viste уже есть GetCurrentProcessorNumber().

Получилось следующее:

unsigned* idt_table;
unsigned idt_table_size;

__declspec(thread) unsigned thread_cache_idt;
__declspec(thread) unsigned thread_cache_proc;

unsigned get_current_processor()
{
#pragma pack(push, 1)
    struct idt_t
    {
        unsigned short size;
        unsigned base;
    };
#pragma pack(pop)

    idt_t idt;
    __sidt(&idt);
    if (idt.base != thread_cache_idt)
    {
        for (unsigned i = 0; i != idt_table_size; ++i)
        {
            if (idt_table[i] == idt.base)
            {
                thread_cache_idt = idt.base;
                thread_cache_proc = i;
                break;
            }
        }
    }
    return thread_cache_proc;
}



1024cores &mdash; all about multithreading, multicore, concurrency, parallelism, lock-free algorithms
Re[4]: попробуй Красную Пилюлю
От: gear nuke  
Дата: 29.03.08 21:05
Оценка: 9 (1)
Здравствуйте, remark, Вы писали:

R>На многопроцессорных/многоядерных WinXP IDT действительно разная на каждый процессор. А есть какие-нибудь гарантии на этот счёт? Чем это обусловлено?


Соотв. функция в WDK позволяет регистрировать ISR для произвольного поцессора:

The IoConnectInterrupt routine registers a device driver's InterruptService routine (ISR), so that it will be called when a device interrupts on any of a specified set of processors.

People who are more than casually interested in computers should have at least some idea of what the underlying hardware is like. Otherwise the programs they write will be pretty weird (c) D.Knuth
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.