· Начало · Отвђтить · Статистика · Поиск · FAQ · Правила · Установки · Язык · Выход · WASM.RU · Noir.Ru ·

 WASM Phorum —› WASM.A&O —› Прав ли Агнер Фог ?

Посл.отвђт Сообщенiе


Дата: Янв 31, 2004 20:06:30

3.4 Use local variables
Variables and objects that are declared inside a function, and not static, will be stored on the stack.
The same applies to function parameters. The memory space occupied by these variables is released when the function returns, and can be reused by the next function.
Using the same memory space again and again makes the caching of memory more efficient. Unless you have very big arrays and objects on your stack, you can be almost certain that your local variables are in the level-1 cache inside the microprocessor, from where they can be accessed many times faster than other parts of the memory. Static and global variables and objects are stored at a fixed place in memory, and are less likely to be cached.

Сабж по данной цитате.Хотелось бы знать мнение более опытных людей, т.к у меня есть соображения
как за , так и против.
P.S. Цитата из pdf с wasm.ru.
P.P.S. Насчет английского: смысл текста ,имхо, прозрачен.


Дата: Фев 1, 2004 06:33:18

Использование статических и глобальных переменных в С - это дурной стиль. Значит, ты не смог сформулировать проблему должным образом. Хотя, конечно, тема спорная. С точки зрения кеширования Фог прав. Как прикажешь такую дрянь кешировать? По поводу стека - если переменных мало, то сработает. А вообще, ход мысли мне нравится :)


Дата: Фев 1, 2004 12:43:34

volodya
Хорошо, если мы активно используем стек, то данная область
памяти будет в кэше. Но нам ведь важен не сам факт кэширования стека, а то, чтобы нужные данные уже были в кэше. Поэтому, передав в функцию указатель на статический
массив можно с большой вероятностью рассчитывать ( особенно если его "поюзать" перед функцией ), что он уже будет в кэше. Локальный же массив, создавшись в ОЗУ, только будет еще загружаться в кэш при обращении к нему.
И потом стек динамически изменяется, значит использовав в
1-ой функции пару int'ов, а в следующей пару массивов из сложных структур, во втором случае мы гарантированно не попадаем в кэш.
Единственное,при передаче констант проц ,наверное, может сразу заслать их в кэш.
Если я не прав, то поправьте.
P.S. Речь не идет о культуре программирования, только физическая сторона вопроса.
P.P.S.
volodya
Насчет хода мыслей, если мой, то польщен, если Фога, тоже
неплохо.:))


Дата: Фев 2, 2004 04:47:54

На слишком активном использовании стека построены многие микроархитектуры, как то: FPU, Java и многие другие RISC. Цитата из Агнера Фога очччень напоминает мне выдержку из документации по RISC-процессорам, так что есть смысл верить :-)


Дата: Фев 2, 2004 14:22:46

Вот как я понимаю работу стека:
Допустим, мы создаем локалиные переменные в стеке. Скажем, 8 DWORD'ов

Место под них резервируется так:
SUB ESP, 20h

Теперь, при любом доступе, например к первой ([ESP]), процессор ЗАГРУЗИТ СТРОКУ КЕША из памяти (это 32 или 64 байта, в зависимости от CPU). Т.е. есть вероятность, что ВСЕ наши переменные будут в кеше. Если ESP кратно 20h, то это будет ОБЯЗАТЕЛЬНО даже при строке кеша 20h байт. Если ESP не кратен 20h (что более вероятно), то, возможно, потребуется еще (всего) одно обращение для полного кешировения данных.

Это частный случай. В более общем, мы все равно имеем кешированные "стековые" данные. Т.е. определенный объем данных стека ВСЕГДА в кеше.

Проблема может быть только при загрузке в эту память значений из других областей памяти, например:
push [Foo]
В этом случае, если dword с адресом Foo не в кеше, то придется ждать, пока она считается (очень долго)..
В случае регистров, непосредственных операндов и переменных вроде [ESP+50h], мы как правило имеем ПРЯМУЮ запись в кеш данных. (А уже потом, эти данные будут выгружены в память)

Т.е. операции со стеком, как правило, очень быстрые.

С другой стороны, если мы ВСЕ наши переменные поместим в одно место в памяти, то они тоже будут в кеше!
Т.е. почти тот же эффект!!
Но здесь есть "скрытый" недостаток: при использовании адресации вроде mov reg, [Foo], мы получаем опкоды бОльшего (!) размера, чем при использовании косвенного mov reg, [EBP]. Это, IMHO, отрицательно сказывается на скорости загрузки команд из памяти (их тоже надо загрузить в кешь) и декодировании.
Т.о. использование стека, IMHO, может дать сразу 2 выигрыша: по скорости и по объему


Проблема, которую здесь сложно учесть - конфликты банков кеша.
Она будет при частом перемещении данных между стеком и НЕКОТОРЫМИ областями памяти, т.е. её можно и избежать. Особенно, есль ВСЕ данные распологать в стеке.

ЗЫ
Тема интересная. Жду замечания/исправления :)


Дата: Фев 3, 2004 09:39:29 · Поправил: HeDiN

Надеюсь приведенный ниже псевдокод вполне понятен.

function( *A[], *B[], *C[], *D[])
{
Temp[];
for( ; ; )
{
Temp[]=rezult( A[], B[], C[]);

}

действия над Temp[] ( сортировка, выкидывание дублей , и т.д.)

for( ; ;)
{
D[]+=Temp[];

}

}

Предположим, function исполняется в цикле, от счетчика которого зависят A[], B[], C[], D[]. Объявив Temp[] перед этим циклом, мы почти гарантируем на всех итерациях, кроме первой, что он будет в кэше. Тогда как в случае локального объявления он каждый раз будет загружаться из оперативки.


Дата: Фев 3, 2004 11:52:15 · Поправил: S_T_A_S_

Надеюсь приведенный ниже псевдокод вполне понятен.
Хм.. дело не в том, что мне (вероятно)это мало понятно. Дело в том, что здесь НЕ ВИДНО стека и кеша :)

Я так понял, объявляя Temp[], просто резервируется место в стеке?
Тогда нет практически никакой разници, где это делается !!
Единственно - косанда SUB ESP, SizeOf(Temp[]) будет выполняться или в цикле или за ним.
Но это не слишкос долго, по сравнению с остальными операциями.
(сортировка и т.п.)


Давайте представим вещи немного в другом свете:
Есть ядро CPU, и есть кеш, будем считать, что это - ОЗУ.
Обычная память - как бы "файл подкачки".
Т.е. процессор НИКОГДА не работает с памятью НАПРЯМУЮ (за исключением некоторых команд SSE/2)
Все команды CPU читает из кеша команд, данные - из кеша данных.

Когда данных в кеше нет, то происходит их "подкачка" из обычной памяти, причем блоками, равными длинне строки кеша (еще есть и аппаратная предвыборка, иногда делающая это заранее)
Таким образом, выделяя ПУСТОЙ локальный (еще - процессор не видит разницы между локальным и глобальным) массив в стеке, мы ВСЕГДА выделяем его в кеше.
Пенальти по скорость мы можем получить, когда данные в етот "массив" надо будет "подгружать" из медленного ОЗУ.

Так что в вашем примере большой разници нет, где объявлять Temp[]
Конечно, разумно выносить "лишние" команды за цикл. Но это - общий принцип оптимизации


ЗЫ
Это все IMHO. Возможно я и заблуждаюсь, поскольку нигде не нашел объяснения как работает кеш "на пальцах" :)


Powered by miniBB 1.6 © 2001-2002
Время загрузки страницы (сек.): 0.397