Здравствуйте, Marty, Вы писали:
E>>ну, например, как ты думаешь, если вот тупо взять и написать на плюсах std::vector<char> и начать в него добавлять по одному, миллион символов добавить удостся?..
M>А ты этим что хотел доказать?
Я задал вопрос, я, например, думал и думаю, что метров до 100 можно не парится, потом могут начаться варианты...
Дык о том и речь...
Жаль, что мы так и не услышали начальника транспортного цеха...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Ikemefula, Вы писали:
EP>>Если ты утверждаешь что это фрагментация, то попробуй сделать (или попроси Marty) тот же тест но добавив и v.reserve(1000*1000*680); в начало. I>Конечно фрагментация, т.к. требуемый размер всей свободной памяти больше чем запрашиваемый.
У него не хватает памяти после примерно выделенного размера, когда уже произошло ~50 реаллокаций.
Так вот, раз ты говоришь что дальше ошибка из-за фрагментации — я предлагаю тебе заменить эти ~50 реаллокаций одним reserve'ом, и начать с этой точки, без фрагментации.
То есть у нас будет одна аллокация на 680MB, и мы будем делать push_back, который вызовет попытку реаллокации в не фрагментированной куче.
Здравствуйте, Ikemefula, Вы писали:
EP>>Тут вообще-то другой случай, практически не относящийся к фрагментации EP>>Вот тебе задачка: какого максимального размера удастся получить vector<char>, делая while(true) x.push_back('\0');, без всяких reserve, при grow factor 1.5 и при идеальной стратегии выделения памяти на x32 (учитывая 2GiB доступной памяти)?
I>Это вроде как первый пример Marty , там нет никакого reserve и тд. Но вобще при grow factor 1.5 и идеальной стратегии должна выюзаться почти вся память.
grow factor=1.5, то есть после реаллокации capacity увеличивается в 1.5 раза.
Во время реаллокации в памяти находится и исходный storage, и новый, так как данные нужно перекинуть из одного места, в другое. Если данные посложнее char, то вызываются конструкторы копирования/перемещения, деструктятся старые объекты.
Это означает, что для реаллокации нам нужно всего 2.5x памяти от текущей capacity.
Для 2GB всего свободной памяти оценка сверху получается 2GB/2.5 = сколько посчитаешь сам, и сравнишь с тем что получилось у Marty.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Это был сарказм. Поэтому я и не понял к чему был твой тест в ответ на сообщение Erop'а.
Ну, если б это было серьезно сказано, то я Erop'а опроверг. А если сарказм, то я его мысль просто проиллюстрировал
EP>Что не удивительно, учитывая что у MSVC'шного вектора capacity растёт на 50%. То есть в момент реаллокации в памяти находится 2.5x от текущей capacity.
В этом случае падеж закономерен — адресного пространства 2Гб не хватает. Тут realloc мог бы помочь, Ikemefula про него был прав.
EP>Мне интересен был твой результат.
А чем интересен именно мой результат, а не любой другой результат, полученный тем же кодом?
Мой результат такой (код в конце):
Тупое выделение куска памяти и освобождение (тест возможности аллокации куска памяти максимального размера):
...
Try to reserve 1900000000 or more chars
Memory reserved
Char at random position: 'W'
Test passed
Try to reserve 2000000000 or more chars
Error: bad allocation
Тесты на фрагментацию
Заполнение вектора char по одному элементу (если увеличить лимиты, у меня выскакивает bad_alloc):
...
i = 649998336
i = 649999360
Char at random position: 'R'
V1 capacity: 689596368
V1 next allocation size prediction: 1034394552
V1 next allocation size prediction and currently allocated space: 1723990920
Заполнение вектора char по одному элементу, затем резервирование двойного размера (если увеличить лимиты, у меня выскакивает bad_alloc):
...
i = 439998464
i = 439999488
Char at random position: 'R'
V1 capacity: 459730912
Vector 2
Memory reserved
First copy added
Second copy added
Char at random position in vector 2: ')'
V2 capacity: 880010000
V1+V2 capacity: 1339740912
Вообщем, Ikemefula во многом прав, по крайней мере стандартный менеджер памяти от MSVS2005 не слишком хорош; также алгоритм резервирования памяти у вектора мог бы быть по-скромнее — начиная с какого-то лимита (например, 300мб на 32 битах) можно резервировать не 1.5, а 1.1, к примеру, а если достигаем 500мб — то можно хапать почти все оставшееся (ясно, что программа явно содержит один гигантский кусок данных, а остальное — мелочь в любом случае), не 1.1 — 1.5, а 2-3, а остатки на мелочь оставить.
Здравствуйте, Ikemefula, Вы писали:
I>>>Своим примером ты показал, что VirtualAlloc ничем не помогает, а помогает grow factor 1.5 и то, слабовато, т.е. все равно всё сдохло. То есть, все работает, как и должно. V>>Ты утверждал, что все заткнется из-за дефрагментации, не? V>>И облажался, по результатам теста. Если в тесте после грубо 660 метров не удалось выделить память, то это значит, что всего было запрошено порядка 660*3=1980 метров. ЧТД. I>А почему на три надо помножать, если добавляем по одному байту ?
Если добавляем по одному байту к capacity, то получаем квадратичную сложность Я даже видел такие реализации (но то был не vector)
Умножать на три нужно при grow factor = 2
Вышенаписанное вообщем-то подтверждает точку зрения Ikemefula, что некоторые менеджеры памяти для C++ не совсем совершенны. Но чтобы он не слишком радовался своей правоте, предлагаю ему привести аналогичные тесты его любимого языка с его любимым GC, чтобы было о чем дальше дискутировать.
PS Ну и да, менеджер от MSVS2005 (по крайней мере), не совершенен. Было бы круто, если бы была возможность у приложения при старте задавать ему хинты — типа мы будем использовать очень большой кусок памяти единовременно, а остатки используй на мелочевку. Ну или вообще — приложение же свое, для основной мега задачи резервируй сколько по максимуму, а крошки пусть ММ оставит на текущие расходы.
Здравствуйте, Ikemefula, Вы писали:
I>Егор прочитал непойми где что "VirtualAlloc помогает". Объяснить, как именно помогает VirtualAlloc, он не смог, ограничился "Почитай Рихтера" и "читай как устроены хипы"
Я признаться, тоже не понял, как VirtualAlloc поможет. Тут скорее нужен свой аллокатор с использованием memory mapped files.
I>Своим примером ты показал, что VirtualAlloc ничем не помогает, а помогает grow factor 1.5 и то, слабовато, т.е. все равно всё сдохло. То есть, все работает, как и должно.
А ты покажи, как GC на раз решает проблемы
Здравствуйте, Ikemefula, Вы писали:
I>Эх, чОрт, не помог VirtualAlloc
А чему он, по твоему, не помог?..
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Marty, Вы писали:
EP>>Что не удивительно, учитывая что у MSVC'шного вектора capacity растёт на 50%. То есть в момент реаллокации в памяти находится 2.5x от текущей capacity. M>В этом случае падеж закономерен — адресного пространства 2Гб не хватает. Тут realloc мог бы помочь, Ikemefula про него был прав.
Падение закономерно, потому что у тебя съедается практически вся доступная память, а не из-за фрагментации.
EP>>Мне интересен был твой результат. M>А чем интересен именно мой результат, а не любой другой результат, полученный тем же кодом?
1. достаточно независимый
2. раз уже был один результат, не хотел вводить путаницу
3. показать лёгкость замены вектора на деку (которой afaik нет в стандартном .net'е)
M>Мой результат такой (код в конце):
M>Тесты на фрагментацию M>Заполнение вектора char по одному элементу (если увеличить лимиты, у меня выскакивает bad_alloc): M>
M>...
M>i = 649998336
M>i = 649999360
M>Char at random position: 'R'
M>V1 capacity: 689596368
M>V1 next allocation size prediction: 1034394552
M>V1 next allocation size prediction and currently allocated space: 1723990920
M>
Сделай тоже самое, но добавив в начало v.reserve(1000*1000*700) — и ты увидишь, что это не фрагментация.
M>Вообщем, Ikemefula во многом прав
В чём-то он прав. И в тех случаях где он действительно прав — я с ним согласен (пруф
).
Но в этом конкретном случае — он не прав, тут проблема не во фрагментации
M>по крайней мере стандартный менеджер памяти от MSVS2005 не слишком хорош; также алгоритм резервирования памяти у вектора мог бы быть по-скромнее — начиная с какого-то лимита (например, 300мб на 32 битах) можно резервировать не 1.5, а 1.1, к примеру, а если достигаем 500мб — то можно хапать почти все оставшееся (ясно, что программа явно содержит один гигантский кусок данных, а остальное — мелочь в любом случае), не 1.1 — 1.5, а 2-3, а остатки на мелочь оставить.
Допустим для x64 — ты какой бы порог взял? Для некоторых приложений 1.1 после 500MB было бы слишком тормозно.
А там где реально нужна экономия, всегда можно делать reserve вручную, с какой угодно стратегией.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
I>>Это вроде как первый пример Marty , там нет никакого reserve и тд. Но вобще при grow factor 1.5 и идеальной стратегии должна выюзаться почти вся память.
Неправильный ответ.
EP>Это означает, что для реаллокации нам нужно всего 2.5x памяти от текущей capacity. EP>Для 2GB всего свободной памяти оценка сверху получается 2GB/2.5 = сколько посчитаешь сам, и сравнишь с тем что получилось у Marty.
Правильный ответ.
Если быть точным, у меня 650Мб получилось против теоретических 800, но это погрешности. Но тут еще зависит от изначального размера, от которого потом grow идет — т.е если сейчас 650мб текущего размера требует для переаллокации 975мб непрерывного куска и 1625мб памяти всего, то если бы было на последнем шаге 600 — 600*1.5+600 = 1500 — данных влезло бы на 900мб.
А вообще, было бы круто, если бы аллокаторы поддерживали изменение grow factor, и его можно было бы поменять через интерфейс контейнера. Что ни говори, а 32 бита еще не скоро сойдут с дистанции, а объемы данных растут постоянно. Закиньте что ли предложение в комитет
Здравствуйте, Marty, Вы писали:
M> Вышенаписанное вообщем-то подтверждает точку зрения Ikemefula, что некоторые менеджеры памяти для C++ не совсем совершенны.
В твоём примере как раз видно, что если и есть фрагментация, то мизерная. Теоретический потолок — 2GiB/2.5, причём это без dll'ек, без стэка и т.п.
Посмотри, например, по каким адресам у тебя dll'ки загрузились, куда .exe, где стэк находится и т.п
Здравствуйте, vdimas, Вы писали:
I>>Своим примером ты показал, что VirtualAlloc ничем не помогает, а помогает grow factor 1.5 и то, слабовато, т.е. все равно всё сдохло. То есть, все работает, как и должно.
V>Ты утверждал, что все заткнется из-за дефрагментации, не? V>И облажался, по результатам теста. Если в тесте после грубо 660 метров не удалось выделить память, то это значит, что всего было запрошено порядка 660*3=1980 метров. ЧТД.
Ты тут не прав
660 метров занято. Если grow 1.5 — требуется еще один непрерывный кусок ~1Гб; вместе с уже аллоцированным — 1.6-1.7Гб, но это два куска, это максимум расхода памяти в процессе переалокации; после реаллока будет занято 1Гб. Ну и если при занятом куске в 660Мб нет места для цельного куска 1Гб — память таки да, фрагментирована, при том, что под данные у Win32 процесса обычно есть честные 2Гб.
Здравствуйте, Marty, Вы писали:
M>Я признаться, тоже не понял, как VirtualAlloc поможет. Тут скорее нужен свой аллокатор с использованием memory mapped files.
Я же уже писал.
Есть два эффекта.
1) Когда куче не хватает памяти, она кусает по VA ещё сегмент, в котором проолжает нарезать блоки, потом ещё сегмент, ещё сегмент и т. д. Трудность в том, что эти сегменты трудно освобождать, так как надо понять, что там нет занятых блоков...
2) В программе может быть несколько компонент, пользующихся разными аллокаторами. И тогда все проблемы с сегментами из (1) или ещё какими возводятся в квадрат, и тогда вот и наступает клизма с фрагментацией АП.
Но, если все аллокаторы в программе аллокируют слишком большие куски сразу по VA, то тогда их можно освобождать, вопервых, и они могут переиспользоваться потом из ДРУГИХ аллокаторов, во-вторых...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Evgeny.Panasyuk, Вы писали:
M>> Вышенаписанное вообщем-то подтверждает точку зрения Ikemefula, что некоторые менеджеры памяти для C++ не совсем совершенны.
EP>В твоём примере как раз видно, что если и есть фрагментация, то мизерная. Теоретический потолок — 2GiB/2.5, причём это без dll'ек, без стэка и т.п. EP>Посмотри, например, по каким адресам у тебя dll'ки загрузились, куда .exe, где стэк находится и т.п
Мизер не мизер, но память делит как минимум на две половины, из которых целое не склеить. Стек — да, он скорее всего однозначно в АП пользовательских данных процесса. По поводу dll-ек — утверждать ничего не буду, но они разве не в другие 2Гб АП грузятся? Или те 2Гб только на нужды ядра и user-space dll там не селятся? Тогда да, надо бы пройтись еще по dll-кам процесса, где они лежат и чье место на самом деле занимают.
Но мне это делать лень Я свою задачу выполнил — подкинул на вентилятордал повод для дальнейших исследований и поисков истины уж простите, но дискуссия интересная, да и давно я в проф. форуме не обозначался
Здравствуйте, Marty, Вы писали:
I>>Своим примером ты показал, что VirtualAlloc ничем не помогает, а помогает grow factor 1.5 и то, слабовато, т.е. все равно всё сдохло. То есть, все работает, как и должно. M>А ты покажи, как GC на раз решает проблемы
Это не интересно, как GC перемещает объекты. Интереснее, как можно выделить 20мб и при этом GC не сможет выделить ни байта, хотя свободной памяти будет ажно 20гб
Здравствуйте, Evgeny.Panasyuk, Вы писали:
EP>Здравствуйте, Marty, Вы писали:
EP>>>Что не удивительно, учитывая что у MSVC'шного вектора capacity растёт на 50%. То есть в момент реаллокации в памяти находится 2.5x от текущей capacity. M>>В этом случае падеж закономерен — адресного пространства 2Гб не хватает. Тут realloc мог бы помочь, Ikemefula про него был прав.
EP>Падение закономерно, потому что у тебя съедается практически вся доступная память, а не из-за фрагментации.
Да, но realloc, который при 1.5 grow factor требует только того, чтобы сразу за 1 используемой памяти было доступно еще 0.5, не упал бы. Тут нужен оператор renew Напишите кто-нибудь в комитет
EP>>>Мне интересен был твой результат. M>>А чем интересен именно мой результат, а не любой другой результат, полученный тем же кодом?
EP>1. достаточно независимый EP>2. раз уже был один результат, не хотел вводить путаницу
Ок
EP>3. показать лёгкость замены вектора на деку (которой afaik нет в стандартном .net'е)
Дека не гарантирует непрерывности куска памяти, а вектор гарантирует. В данном случае деке пофигу, но если непрерывность критична (насчет .Net не знаю) — то ой.
M>>Мой результат такой (код в конце):
M>>Тесты на фрагментацию M>>Заполнение вектора char по одному элементу (если увеличить лимиты, у меня выскакивает bad_alloc): EP>Сделай тоже самое, но добавив в начало v.reserve(1000*1000*700) — и ты увидишь, что это не фрагментация.
А что это? первый тест не смог 2Гб выделить, но 1.9Гб одним куском — смог. Впрочем — да, наверно просто не хватает места на переаллокацию. Но reserve в начале просто вычеркнет этот тест из тестов на фрагментацию, и впишет его в тесты на макс объем доступной памяти из п.1.
M>>Вообщем, Ikemefula во многом прав
EP>В чём-то он прав. И в тех случаях где он действительно прав — я с ним согласен (пруф
).
Лень смотреть потом гляну.
EP>Но в этом конкретном случае — он не прав, тут проблема не во фрагментации
Да, скорее не во фрагментации, чем во фрагментации.
M>>по крайней мере стандартный менеджер памяти от MSVS2005 не слишком хорош; также алгоритм резервирования памяти у вектора мог бы быть по-скромнее — начиная с какого-то лимита (например, 300мб на 32 битах) можно резервировать не 1.5, а 1.1, к примеру, а если достигаем 500мб — то можно хапать почти все оставшееся (ясно, что программа явно содержит один гигантский кусок данных, а остальное — мелочь в любом случае), не 1.1 — 1.5, а 2-3, а остатки на мелочь оставить.
EP>Допустим для x64 — ты какой бы порог взял? Для некоторых приложений 1.1 после 500MB было бы слишком тормозно.
Ну, для 64 бит пока вроде не проблемы фрагментации АП как класса задач А для 32 — да, тормозно, но ковыляло бы
EP>А там где реально нужна экономия, всегда можно делать reserve вручную, с какой угодно стратегией.
Сейчас запустил тест с reserve и проверкой capcity — пока не закончился, но предполагаю, что capcity будет больше reserve. Результат отпишу чуть позже
Здравствуйте, Marty, Вы писали:
M>>> Вышенаписанное вообщем-то подтверждает точку зрения Ikemefula, что некоторые менеджеры памяти для C++ не совсем совершенны. EP>>В твоём примере как раз видно, что если и есть фрагментация, то мизерная. Теоретический потолок — 2GiB/2.5, причём это без dll'ек, без стэка и т.п. EP>>Посмотри, например, по каким адресам у тебя dll'ки загрузились, куда .exe, где стэк находится и т.п M>Мизер не мизер, но память делит как минимум на две половины, из которых целое не склеить.
Это не фрагментация.
Фрагметация — это пример который приводил Ikemefula раньше — там где посредине АП есть что-то небольшое, что мешает выделить большой кусок.
Тут же просто нехватка памяти. Если бы те ~50 аллокаций что были у тебя действительно фрагментировали бы кучу — то аллокация сфейлилась бы гораздо раньше.
M>Стек — да, он скорее всего однозначно в АП пользовательских данных процесса. По поводу dll-ек — утверждать ничего не буду, но они разве не в другие 2Гб АП грузятся? Или те 2Гб только на нужды ядра и user-space dll там не селятся? Тогда да, надо бы пройтись еще по dll-кам процесса, где они лежат и чье место на самом деле занимают. M>Но мне это делать лень Я свою задачу выполнил — подкинул на вентилятордал повод для дальнейших исследований и поисков истины уж простите, но дискуссия интересная, да и давно я в проф. форуме не обозначался
На MSVC2010SP1x32 (не знаю что там у тебя) самый низкий адрес у msvcp100.dll — 0x664C0000. Верх .exe'шника — 0x00A27000. ESP = 0x0140FBC0.
Разница между msvcp100.dll и ESP ~ 1695MB (мегабайт, не мебибайт). Делим на 2.5: 678 MB — что-то знакомое
Здравствуйте, Marty, Вы писали:
EP>>>>Что не удивительно, учитывая что у MSVC'шного вектора capacity растёт на 50%. То есть в момент реаллокации в памяти находится 2.5x от текущей capacity. M>>>В этом случае падеж закономерен — адресного пространства 2Гб не хватает. Тут realloc мог бы помочь, Ikemefula про него был прав. EP>>Падение закономерно, потому что у тебя съедается практически вся доступная память, а не из-за фрагментации. M>Да, но realloc, который при 1.5 grow factor требует только того, чтобы сразу за 1 используемой памяти было доступно еще 0.5, не упал бы. Тут нужен оператор renew Напишите кто-нибудь в комитет
Почему бы и нет
EP>>3. показать лёгкость замены вектора на деку (которой afaik нет в стандартном .net'е) M>Дека не гарантирует непрерывности куска памяти, а вектор гарантирует. В данном случае деке пофигу, но если непрерывность критична (насчет .Net не знаю) — то ой.
Непрерывность вектора действительно иногда бывает полезна сама по себе. Но чаще она полезна просто как самый быстрый кусок данных.
Ikemefula вроде говорил что у него где-то на .net с vector-like были проблемы, да причём такие, что он теперь векторов как огня боится. Вот я и удивляюсь — почему в .net нет стандартной деки (или всё-таки есть?).
M>>>Мой результат такой (код в конце):
M>>>Тесты на фрагментацию M>>>Заполнение вектора char по одному элементу (если увеличить лимиты, у меня выскакивает bad_alloc): EP>>Сделай тоже самое, но добавив в начало v.reserve(1000*1000*700) — и ты увидишь, что это не фрагментация. M>А что это? первый тест не смог 2Гб выделить, но 1.9Гб одним куском — смог. Впрочем — да, наверно просто не хватает места на переаллокацию.
Это возможность убрать ~49 первых реаллокаций и убедится что дело не в них. (с обсуждения этих реаллокаций как раз и начинался этот топик)
M>>>по крайней мере стандартный менеджер памяти от MSVS2005 не слишком хорош; также алгоритм резервирования памяти у вектора мог бы быть по-скромнее — начиная с какого-то лимита (например, 300мб на 32 битах) можно резервировать не 1.5, а 1.1, к примеру, а если достигаем 500мб — то можно хапать почти все оставшееся (ясно, что программа явно содержит один гигантский кусок данных, а остальное — мелочь в любом случае), не 1.1 — 1.5, а 2-3, а остатки на мелочь оставить. EP>>Допустим для x64 — ты какой бы порог взял? Для некоторых приложений 1.1 после 500MB было бы слишком тормозно. M>Ну, для 64 бит пока вроде не проблемы фрагментации АП как класса задач А для 32 — да, тормозно, но ковыляло бы
Я думал мы тут уже не об фрагментации говорим, а об свободной памяти — она ведь не бесконечна. И точно такая же проблема возможна и на x64, кстати, как раз потому, что дело не во фрагментации.
Здравствуйте, Evgeny.Panasyuk, Вы писали:
M>>>> Вышенаписанное вообщем-то подтверждает точку зрения Ikemefula, что некоторые менеджеры памяти для C++ не совсем совершенны. EP>>>В твоём примере как раз видно, что если и есть фрагментация, то мизерная. Теоретический потолок — 2GiB/2.5, причём это без dll'ек, без стэка и т.п. EP>>>Посмотри, например, по каким адресам у тебя dll'ки загрузились, куда .exe, где стэк находится и т.п M>>Мизер не мизер, но память делит как минимум на две половины, из которых целое не склеить.
EP>Это не фрагментация. EP>Фрагметация — это пример который приводил Ikemefula раньше — там где посредине АП есть что-то небольшое, что мешает выделить большой кусок.
Я вроде под фрагментацией тоже самое и подразумеваю. Или не?
EP>Тут же просто нехватка памяти. Если бы те ~50 аллокаций что были у тебя действительно фрагментировали бы кучу — то аллокация сфейлилась бы гораздо раньше.
Вопрос насколько они кучу сфрагментировали, может совсем чуть-чуть, только в серединке
M>>Стек — да, он скорее всего однозначно в АП пользовательских данных процесса. По поводу dll-ек — утверждать ничего не буду, но они разве не в другие 2Гб АП грузятся? Или те 2Гб только на нужды ядра и user-space dll
EP>На MSVC2010SP1x32 (не знаю что там у тебя) самый низкий адрес у msvcp100.dll — 0x664C0000. Верх .exe'шника — 0x00A27000. ESP = 0x0140FBC0. EP>Разница между msvcp100.dll и ESP ~ 1695MB (мегабайт, не мебибайт). Делим на 2.5: 678 MB — что-то знакомое
Я писал, у меня MSVC2005, но думаю, особых изменений с тех пор не было.
Ок, похоже на правду Как бы прошу заметить, что я тут независимое расследование провожу, так что попрошу без подxyzколок