Здравствуйте, alexeiz, Вы писали:
A>Столкнулся недавно с интересной проблемой, которая себя очень странно проявляла, но в конце концов оказалась следствием ошибки алгоритмического характера.
A>Есть класс (C++), представляющий из себя коллекцию некоторых записей. Назовем его Rowset....
A>Внимание, вопрос: в чем может быть проблема и как ее искать?
"Дядя, а я знаю что у вас сломалось"
В программе, в чем же еще может быть проблема.
Я у себя (в страничном кэше с поддержкой многопоточного доступа) похожие тормоза искал с помощью тестов. Нашел линейней поиск в списке грязных страниц, переделал его на поиск через хеш — все стало "как у взрослых"
Искал очень долго ....
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, Коваленко Дмитрий, Вы писали:
КД>"Дядя, а я знаю что у вас сломалось"
КД>Я у себя (в страничном кэше с поддержкой многопоточного доступа) похожие тормоза искал с помощью тестов. Нашел линейней поиск в списке грязных страниц, переделал его на поиск через хеш — все стало "как у взрослых"
это не объясняет разное поведение на различных платформах.
Здравствуйте, Сергей Мухин, Вы писали:
СМ>Здравствуйте, valaar, Вы писали:
V>>в первом сообщении автор же написал "Столкнулся недавно с интересной проблемой, которая себя очень странно проявляла, но в конце концов оказалась следствием ошибки алгоритмического характера." V>>то есть ошибка уже найдена и устранена, но предлагает подумать и предложить версии, что могло быть причиной
СМ>вы вообще понимаете, что нам НИЧЕГО не предложили. Как можно искать алгоритмическую ошибку не зная, даже примерно, алгоритма? СМ>И как алгоритм может зависеть от платформы (хотя выбор алгоритма может)? Ошибка в реализации алгоритма.
СМ>т.е. если помочь человеку или нельзя или поздно (раз сам решил). Жаль время.
Во-первых, пока автор поступал честно. Надеюсь, что правильный ответ через некоторое время тоже сообщит.
Во-вторых, вопрос был не только "в чём может быть ошибка?", но и "как её искать?". Высказывайте свои версии и возможные инструменты, автор подсказывал как они будут себя вести. В чём-то похоже на текстовое adventure, где есть некоторое описание, а на возможные действия идёт различная реакция.
В-третьих, все уже обращали внимание, что "результат зависит от платформы". Хотя ошибка и была в алгоритме, но почему вела себя по-разному? Здесь алгоритм вообще можно рассматривать как чёрный ящик. Автор упомянул, что это коллекция, потому что без этого поведение по добавлению малого числа записей и большого было бы сложно описать. Бросаться сразу в отладчик и исходники можно, но это путь "грубой силы".
Здравствуйте, alexeiz, Вы писали:
A>Столкнулся недавно с интересной проблемой, которая себя очень странно проявляла, но в конце концов оказалась следствием ошибки алгоритмического характера.
... A>Внимание, вопрос: в чем может быть проблема и как ее искать?
Верно ли что (просьба уточнить):
а) Исходный код на C++ для каждой из платформ (S, L, A, H) компилировался отдельно своим компилятором?
б) Среди платформ L, A, H была платформа той же разрядности, что и платформа S?
в) Специфика платформ не использовалась? (Например, IOCP под windows.)
г) Использовались ли какие-нибудь мультиплатформенные (например, boost) библиотеки, которые могли внутри себя использовать специфику платформ?
Здравствуйте, Сергей Мухин, Вы писали:
КД>>Я у себя (в страничном кэше с поддержкой многопоточного доступа) похожие тормоза искал с помощью тестов. Нашел линейней поиск в списке грязных страниц, переделал его на поиск через хеш — все стало "как у взрослых"
СМ>это не объясняет разное поведение на различных платформах.
Ну, были еще (конкретные) проблемы с STL. Бинарник из под BCB5 летал (Rogue Wave STL?). А из под VS2005-2008 (думаю и с 2010 было бы тоже самое) — еле ползал. Особенно это было видно в многопоточной программе.
Проблема оказалась в std::locale.
Пусть поиграется с разными компиляторами/базовыми библиотеками. Может локализует причину
Один черт — нужны тесты, с демонстрацией стабильной деградации производительности.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, valaar, Вы писали:
V>Верно ли что (просьба уточнить): V>а) Исходный код на C++ для каждой из платформ (S, L, A, H) компилировался отдельно своим компилятором?
На каждой — свой компилятор. S — Solaris/SunCC, L — Linux/GCC, A — AIX/xlC, H — HPUX/aCC.
V>б) Среди платформ L, A, H была платформа той же разрядности, что и платформа S?
Той-же.
V>в) Специфика платформ не использовалась? (Например, IOCP под windows.)
Нет.
V>г) Использовались ли какие-нибудь мультиплатформенные (например, boost) библиотеки, которые могли внутри себя использовать специфику платформ?
Нет. Конечно, у нас есть платформно зависимые библиотеки. Но можно точно сказать, что они там не были задействованы.
Здравствуйте, minorlogic, Вы писали:
M>Тогда и вы скажите , что лежит у меня в кармане ?
M>Другими словами о чем тут думать если нет вводной даже о используемом контейнере ?
Об контейнерах, используемых в этом Rowset'е, я знал на тот момент не больше вашего
Здравствуйте, Сергей Мухин, Вы писали:
СМ>вы вообще понимаете, что нам НИЧЕГО не предложили. Как можно искать алгоритмическую ошибку не зная, даже примерно, алгоритма? СМ>И как алгоритм может зависеть от платформы (хотя выбор алгоритма может)? Ошибка в реализации алгоритма.
Алгоритм не зависит от платформы. Но ошибка в нем проявляется по разному на разных платформах. Вот так иногда бывает.
Здравствуйте, alpha21264, Вы писали:
A>Фрагментацию памяти смотрели? A>Только не спрашивайте как
Так как замена системного менеджера памяти перестает приводить к исчерпанию памяти, то это точно фрагментация. Осталось только найти из-за чего. Как здесь уже посоветовали, каждую аллокацию/деаллокацию записываем в лог. Большой лог получается, 150MB.
Но теперь мы уже не зависим от Rowset и его деталей реализации. Это важный момент. В коде Rowset можно было копаться долго и очень долго.
Проблему можно воспроизвести, проигрывая этот лог. Чем я и хотел заняться следующим делом. Но сначала я полез в него посмотреть, есть ли какой-нибудь цикличеки повторяющийся фрагмент. И он там сразу нашелся в самом начале лога. Около 30 аллокаций/деаллокаций — ничего серьезного. Было трудно поверить, что это и создает злодейскую фрагментацию памяти. Я уже начал писать простенький код, чтобы этот цикл прогонять энное количество раз. Но потом зачем-то полез в конец лога. И там обнаружилось...
alloc 399996
free 399992
... 30 alloc/free, что и в начале
alloc 400000
free 399996
... 30 alloc/free
и так далее
Ну, вот почти и все. Осталось только догадаться, чем в стандартной библиотеке можно создать такой pattern, и почему этот код очень сильно замедлялся на всех остальных платформах, где не наблюдалось фрагментации памяти.
Здравствуйте, alexeiz, Вы писали:
A>Здравствуйте, minorlogic, Вы писали:
M>>Тогда и вы скажите , что лежит у меня в кармане ?
M>>Другими словами о чем тут думать если нет вводной даже о используемом контейнере ?
A>Об контейнерах, используемых в этом Rowset'е, я знал на тот момент не больше вашего
Тогда откуда предположение что проблема в реализации Rowset?
Здравствуйте, alexeiz, Вы писали: A>alloc 399996 A>free 399992 A>... 30 alloc/free, что и в начале A>alloc 400000 A>free 399996 A>... 30 alloc/free A>и так далее A>Ну, вот почти и все. Осталось только догадаться, чем в стандартной библиотеке можно создать такой pattern, и почему этот код очень сильно замедлялся на всех остальных платформах, где не наблюдалось фрагментации памяти.
Была мысль о vector, который в обычно реализации может привести к троекратному перерасходу памяти. Но этого мало, да и не тормозило бы. А вот если в коде используется «оптимизация» (автор вероятно хотел «сэкономить» память), перед добавлением каждого элемента вызывается reserve(size()+1), то получим как раз указанное поведение. Мало того, что оно тормозит на любой платформе, постоянно переаллоцируя и копируя память, так ещё и на одних оно нагружает менеджер кучи дефрагментацией, а на других — валит с перерасходом фрагментированной неиспользуемой памяти.
Может и без reserve, а просто каждый раз создаётся новый вектор, ещё хуже. Может даже не вектор, а wstring.
Здравствуйте, alexeiz, Вы писали:
A>>Фрагментацию памяти смотрели? A>>Только не спрашивайте как
A>Так как замена системного менеджера памяти перестает приводить к исчерпанию памяти, то это точно фрагментация. Осталось только найти из-за чего. Как здесь уже посоветовали, каждую аллокацию/деаллокацию записываем в лог. Большой лог получается, 150MB.
A>Но теперь мы уже не зависим от Rowset и его деталей реализации. Это важный момент. В коде Rowset можно было копаться долго и очень долго.
A>Проблему можно воспроизвести, проигрывая этот лог. Чем я и хотел заняться следующим делом. Но сначала я полез в него посмотреть, есть ли какой-нибудь цикличеки повторяющийся фрагмент. И он там сразу нашелся в самом начале лога. Около 30 аллокаций/деаллокаций — ничего серьезного. Было трудно поверить, что это и создает злодейскую фрагментацию памяти. Я уже начал писать простенький код, чтобы этот цикл прогонять энное количество раз. Но потом зачем-то полез в конец лога. И там обнаружилось...
Здравствуйте, gegMOPO4, Вы писали:
MOP>Была мысль о vector, который в обычно реализации может привести к троекратному перерасходу памяти. Но этого мало, да и не тормозило бы. А вот если в коде используется «оптимизация» (автор вероятно хотел «сэкономить» память), перед добавлением каждого элемента вызывается reserve(size()+1), то получим как раз указанное поведение. Мало того, что оно тормозит на любой платформе, постоянно переаллоцируя и копируя память, так ещё и на одних оно нагружает менеджер кучи дефрагментацией, а на других — валит с перерасходом фрагментированной неиспользуемой памяти.
Так и есть . Хотя, причина была не в желании съэкономить память, а в написании exception-safe кода, и reserve(size() + 1) был завуалирован в reserve(size() + numberOfRows), где numberOfRows всегда равнялось 1.