Как заюзать большие страницы?

Тема в разделе "WASM.WIN32", создана пользователем The_GorYnycH, 21 май 2008.

  1. The_GorYnycH

    The_GorYnycH New Member

    Публикаций:
    0
    Регистрация:
    15 фев 2008
    Сообщения:
    32
    Добрый времени суток!
    OC: пока что Вынь Server 2003 32-bit, потом буду переносить на 2008 64-bit
    Понадобилось мне в приложении использовать большие объемы памяти. Написал несколько тестов, выяснилось, что при записи в память если происходит промах TLB, скорость сильно падает. Эт, конешно, не новость:) Решил попробовать использовать большие страницы, йакобы промахов TLB должно быть меньше, и накладные расходы тоже должны уменьшицо, а скорость возрасти... вызвал VirtualAlloc с соответствующим флагом, память выделилась, но скорость только уменьшилась. Вот и сижу теперь гадаю, что я не так сделал. Собственно вопросы:
    1.Как еще можно использовать в приложении поддержку больших страниц, кроме вызова VirtualAlloc? Можно ли сделать это не только в рамках одного процесса, но и для системы в целом?
    2.Почему наблюдается уменьшение скорости записи в память при использовании больших страниц и прочих равных условиях?

    Если кто-нить использовал большие страницы и получил повышение производительности, научите плз уму-разуму:) Спасибо!
     
  2. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    В юзермоде (вроде) никак. Где ты в VirtualAlloc "соответствующий флаг" нашел ?

    Промахи TLB это фигня по сравнению с отказами страниц, промахами кэша и даже переполнениями очереди в процессоре. Лучше смотри в сторону оптимизации работы с кэшем и памятью (префетчи, сквозная запись movntq и т.п.)

    Большие - это сколько ?
     
  3. diamond

    diamond New Member

    Публикаций:
    0
    Регистрация:
    21 май 2004
    Сообщения:
    507
    Адрес:
    Russia
    leo
    *растерянно* в официальной документации: http://msdn.microsoft.com/en-us/library/aa366887(VS.85).aspx - называется MEM_LARGE_PAGES
    The_GorYnycH
    До XP включительно - никак. Позднее - вот раздел MSDN'а, а конкретную реализацию я видел в исходниках 7-Zip'а (файл Common\Alloc.cpp).
     
  4. The_GorYnycH

    The_GorYnycH New Member

    Публикаций:
    0
    Регистрация:
    15 фев 2008
    Сообщения:
    32
    leo
    ну хз, я наблюдал по крайней мере двухкратное снижение скорости при промахах TLB, а отказов страниц не будет, т.к. отключен файл подкачки. С остальным бороться сложно...
    Вся доступная физическая память.

    Интересно, что во всех приличных книжках описывается, что есть такая штука, как большие страницы, юзайте, и будет вам счастье, а как именно пользовать никто не пишет:dntknw:
     
  5. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    diamond
    Спасибо за инфу

    The_GorYnycH
    Видимо ты отказы страниц и воспринимаешь как промахи TLB. Под промахом TLB в узком смысле понимается отсутствие записи PTE\PDE в TLB процессора или в кэше, и эту запись приходится грузить из ОЗУ. Если же записи нет в ОЗУ или есть, но невалидная, то проц генерит отказ страницы. Файл подкачки тут ни причем, т.к. при VirtualAlloc физическая память реально выделяется только "по требованию" при первом обращении к странице на чтение или запись. При первом обращении ес-но возникает отказ страницы, на обработку которого уходит несколько тысяч тактов, что по времени как раз сравнимо с чтением или записью страницы в ОЗУ. А сами промахи TLB при последовательном чтении\записи это фигня, т.к. загрузка одной-двух записей PDE\PTE из кэша или даже ОЗУ это мизер по сравнению со временем чтения\записи целой страницы данных
     
  6. The_GorYnycH

    The_GorYnycH New Member

    Публикаций:
    0
    Регистрация:
    15 фев 2008
    Сообщения:
    32
    Вообщет файл подкачки как раз "причем", поскольку при вызове VirtualAlloc страницы выделяюцо именно в нем. А при первом обращении к странице для нее создаецо запись в соответствующей таблице страниц, и страничка подсасывается в память, одновременно запись о ней кладется в TLB. При этом, если нагрузка на память высока, и свободных страниц мало или совсем нет, из памяти выгружается в файл подкачки какая-нить страница из рабочего набора того или иного процесса. Операция выгрузки/загрузки страницы синхронная, отсюда и большое время ожидания.

    Забыл сказать, что непосредственно перед измерением скорости памяти я обращаюсь ко всем страницам обнуляя их, что по идее должно приводить к созданию соответствующих записей в таблицах страниц, а значит при последующем моем к ним обращении отказа страницы уже не будет, и время тратится только на подгрузку страницы в TLB, если ее там нет.
     
  7. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Ты какой травы обкурился ? :)
    Почитай что ли Руссиновича или статью Great чтобы понять разницу между Paged out PTE и Demand zero PTE. И пошевели мозгом - как быстрее обнулить страницу по требованию: в памяти со скоростью в несколько Гб/с или записать, а затем считать с диска со скоростью ~50Mb/c ?!! Поэтому в файл подкачки сбрасываются только модифицированные страницы, к коим demand zero не относятся

    Надеюсь ты в курсе, что VirtualAlloc выдает обнуленные страницы...

    В TLB грузится не страница, а запись PTE. Считать умеешь ? Запись PTE 4 байта, итого 16 записей на 64-байтную линейку кэша. Т.е. при одном промахе в кэш будут загружены PTE на 16 страниц. Поэтому при последовательном чтении\записи относительные затраты на подгрузку PTE из ОЗУ будут в худшем случае составлять 64/(4К*16), т.е. те же 4/4К = 1/1024 от времени чтения или записи данных в страницу. В действительности же если используется обычная кэшируемая запись, то относительные затраты на TLB будут по крайней мере вдвое меньше, т.к. при обычной записи проц сначала грузит линейку из ОЗУ, пишет данные в кэш, а затем при недостатке места сбрасывает модифицированные данные в ОЗУ, т.е. время обмена с ОЗУ увеличивается вдвое, т.к. страница сначала читается, а затем записывается обратно. Поэтому если в страницу данные только пишутся (а не модифицируются существующие), то для исключения лишнего потока чтения страницы в кэш можно использовать сквозную запись movntX (non-temporal) - при больших объемах записываемых данных это получается существенно быстрее
     
  8. The_GorYnycH

    The_GorYnycH New Member

    Публикаций:
    0
    Регистрация:
    15 фев 2008
    Сообщения:
    32
    тебе скажи, аццкий ты сотона:))) Курил Руссиновича, и много чего еще:)))

    У рихтера на 332 странице про VirtualAlloc почетай. Попробуй передать с помощью VirtualAlloc где-нить с гиг памяти, увидишь, что размер файла подкачки увеличится соразмерно этой величине. Я просто некорректно выразился, когда писал, что при первом обращении страничка подсасывается в память - не имел ввиду, что она физически копируется с файла подкачки, нахрена ее копировать, если там ничо нет. Она, само собой выделяется из страниц физической памяти, а в файл подкачки сбрасывается модифицированная страница.

    Ага, в курсе... Без разницы, я их обнулял просто чтоб к ним обратицо. Мне удобнее написать memset() чем в цикле перебирать страницы. Мог бы в принципе записать в первый байт каждой страницы любое значение - для меня эффект был бы тот же.

    За сцылко спасибо, щаз почитаю.

    Может объяснишь мне, почему при выключеном файле подкачки, скорость записи в память, выделенную с помощью VirtuallAlloc размером 64-512К в три раза больше, чем скорость записи в память размером 16М-и более? Причем к памяти предварительно обращался, т.е. по идее все записи должны присутствовать в таблице страниц.
     
  9. leo

    leo Active Member

    Публикаций:
    0
    Регистрация:
    4 авг 2004
    Сообщения:
    2.542
    Адрес:
    Russia
    Я уже пытался объяснить, только ты видать мимо ушей пропустил :lol:
    При обычной кэшируемой (write-back) записи в память через mov\movs процессор сначала читает данные по запрашиваемым адресам из ОЗУ в кэш, а затем записывает в кэш по этим адресам новые данные. Если измененные данные умещаются в кэш, то они могут продолжать в нем сидеть неопределенно долго, пока не будут вытеснены другими чтениями\записями твоего или другого потока. Поэтому если замерять время записи небольших объемов данных, то непосредственно запись данных в ОЗУ тут может вообще не фигурировать. Причем если ты уже "к памяти предварительно обращался", то данные уже могут быть в кэше (все или часть), тогда и поток чтения из ОЗУ исключается и измеренная "скорость записи" может быть вообще "фантастической".
    Если же писать "16М и более", то на 100% будет два конкурирующих потока обмена с ОЗУ: чтение одних линеек в кэш и запись модифицированных линеек из кэша в ОЗУ. Т.е. как минимум в 2 раза медленнее, чем просто чтение, а реально м.б. заметно хуже, т.к. эти потоки мешают друг другу, нарушая последовательный доступ к ОЗУ (т.к.запись производится только при вытеснении запросом на чтение новых данных из далеко отстоящей страницы)

    PS: Попробуй записывать большие объемы данных через movntq - должно быть существенно быстрее, чем обычными mov\movs
     
  10. The_GorYnycH

    The_GorYnycH New Member

    Публикаций:
    0
    Регистрация:
    15 фев 2008
    Сообщения:
    32
    Ага, реально пропустил:dntknw:
    Ну так вроде все правильно ты говоришь, статейки почитал, все сходится, спасибо за совет!:)
    На асме ничо не писал, не дашь кусочек кода для примера?
     
  11. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Охх, заблуждаетесь вы, товарищ..
    VirtualAlloc делает все действия в памяти.
    Когда ты выделяешь память с доступом > PAGE_NOACCESS, создается Virtual Address Descriptor, описывающий твою память, записываются недействительные Demand-Zero PTE в соответствующие элементы таблиц страниц.
    При первом обращении возникает отказ страницы, обработчик MmAccessFault достает физическую страницу из списка ZeroedPageList и делает для нее валидный PTE заместо demand-zero PTE. Виртуальная страница становится валидной.
    Далее, если ты ее ни разу не модифицировал, то в случае нехватки памяти твоя страница в своп НЕ пойдет - винда увидит, что у тебя чистая страница и ты в нее ничего не писал, ее переместят обратно в список свободных страниц, а тебе запишут Demand-Zero (или transition, точно не помню) PTE.
    Если ты ее модифицировал, она пойдет в список модифицированных страниц, а PTE превратится в недействительный Transition PTE, дальше нужное число страниц будет скинуто в страничный файл, страницу переведут в список свободных/обнуленных, а твой PTE в paged-out PTE

    Да, кстати, забыл сказать - нет вообще такого понятия, как "выделение" памяти в файле подкачки.
    Страницы туда скидываются только по требованию ОС, в оперативной памяти поддерживается битовая карта занятости страниц файла подкачки, в ней ищетяс нужное число нулевых битов, страницы переводятся в занятое состояние и записываются по соответствующим смещениям в файл через IoAsynchronousPageWrite().
     
  12. jhons

    jhons New Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2007
    Сообщения:
    26
    можно вопрос ... а вы "где"? тута ребята спорят про невыгружаемые страницы (как основной тормоз) , а это токма в кернел моде юзать можно (да хоть ваще "линейно"... но это в темы кернел моде плиз, вместе с IoAsynchronousPageWrite() до толпы) ... вроде, по другому не знаю.
    зы: почитал (спасибо leo) про AdjustTokenPrivileges ... но тож про невыгружаемость ничего не нашел ... хм.
    про другой метод ? ... есть такая штука - "куча". проще репы.. если про "накладные расходы" говорить...
    Код (Text):
    1.             invoke  GetProcessHeap
    2.             mov hheap,eax
    3.             invoke  HeapAlloc,hheap,0,file_size
    4.             mov pmemory,eax; указатель на кучу размером file_size
    5.  
    6.                                       ................................
    7.             invoke  HeapFree,hheap,0,pmemory
    скока хочешь выделяет до Га - точно.. но тока в контексте, пардон. но невыгружаемость тоже не гарантирует , однако.
    зы: а ваще можно написать системный сервис , оный выделяет невыгружаемую память , оную потом можно дергать из различных процессов хоть напрямую - через отображение ее в адресном пространстве процесса ... но это с "накладными расходами" вариант.
     
  13. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    зачем изобретать велосипед, если есть VirtualLock
     
  14. jhons

    jhons New Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2007
    Сообщения:
    26
    да вроде
    трансляция одного участка памяти для различных процов... так понял.
    зы: хотя не знаю ... мож где есть что то типа GetCommonAlloc().
    а про сие .. http://www.cyberguru.ru/cpp-sources/win32/virtuallock-i-virtualunlock.html . н-да ... буду читать справочники. спасиб.
     
  15. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    Перечитай, я написал не про VirtualAlloc, а про VirtualLock
     
  16. jhons

    jhons New Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2007
    Сообщения:
    26
    перечитал , все правильно ... )))

    "GetCommonAlloc()" типа "дать указатель ОБЩУЮ (для процов) память". локализовать ее от сброса (lock) в подкачку ... уже второстепеннее. сходи по ссылке.
     
  17. wasm_test

    wasm_test wasm test user

    Публикаций:
    0
    Регистрация:
    24 ноя 2006
    Сообщения:
    5.582
    хм недочитал твой пост просто, сори)
     
  18. The_GorYnycH

    The_GorYnycH New Member

    Публикаций:
    0
    Регистрация:
    15 фев 2008
    Сообщения:
    32
    Действительно есть... действительно проще репы... но не подходит.
    Не так. Имелось ввиду, можно ли заставить ОС работать только с большими страницами. Просто получается, что единственный способ их заюзать в user mode - вызвать VirtualAlloc с флагом MEM_LARGE_PAGES. Думал, может кто знает альтернативный путь. Поскольку я все равно не смог добиться хоть какого-то прироста производительности используя большие страницы, решил наваять несколько тестов, использовать разные режимы записи, как leo советовал. Неохота велосипед изобретать, если кто делал, поделитесь кодом пожалуйста.

    Память я лочу вызовом VirtualLock, забыл написать про это, сорри.
     
  19. jhons

    jhons New Member

    Публикаций:
    0
    Регистрация:
    23 ноя 2007
    Сообщения:
    26
    пардон ... ваше объяснение "большие страницы" - это
    нескока сбивает с толку ... "большие страницы" это страницы которые по 16 метров , помойму. и для всей системы где то в .ini задаются.
    хм так ... перечитал еще. вас смущает "тормоз" создания новых страниц к процессу ? .. что мешает загодя обнулить их ручками , например, (инициировать , обратясь к ним) .. тогда они уже будут висеть в системе.
     
  20. _basmp_

    _basmp_ New Member

    Публикаций:
    0
    Регистрация:
    10 июл 2005
    Сообщения:
    2.939
    The_GorYnycH
    Так эта вся пурга для роста производительности? В свое время играясь с ХипАллок-ом получал 10-ти кратный в среднем прирост производительности, но до залибленья не довел из-за ненужности мне этой вещи. А большие страницы (если они выделятся вообще. имхо в выни они не реализованы) только замедлить должны.