Почему так долго происходит инициализация нулями?
и да — почему не использовать memset вместо цикла?
Надеюсь, не отладочную версию собираешь?
http://slil.ru/28976096
Там для снятия замеров используется сторонняя библиотека, которая вряд ли у вас есть, так что скомпилировать, видимо, не получится.
Мемсет не использую, потому что нет привычки. Собираю релиз-версию.
Вот код: Там для снятия замеров используется сторонняя библиотека, которая вряд ли у вас есть, так что скомпилировать, видимо, не получится.
Мемсет не использую, потому что нет привычки. Собираю релиз-версию.
float [10000][10000][2]Может лучше std::vector использовать?
Использовал три вложенных std::vector'а - они вообще не позволяют столько памяти выделить, вылетают ещё до самой первой команды.
Попробуй дебаг собрать. У меня на float f[10000][10000][2]; в дебаге выдается overflow, что неудивительно.
1. Инициализации нет:
krasin:~/Downloads$ time ./a.out
1.Simple, serial calculation let's start!
real 0m2.277s
user 0m1.668s
sys 0m0.516s
2. Инициализация циклом
for(int k = 1; k < Nx; k++)
{
for(int l = 1; l < Ny; l++)
{
u[k][l][0]=0;
u[k][l][1]=0;
}
}
krasin:~/Downloads$ time ./a.out
1.Simple, serial calculation let's start!
real 0m3.321s
user 0m2.548s
sys 0m0.480s
3. Инициализация memset-ом
memset(u, 0, Nx * Ny * 2 * sizeof(float;
krasin:~/Downloads$ time ./a.out
1.Simple, serial calculation let's start!
real 0m2.580s
user 0m2.040s
sys 0m0.368s
Вывод: пользуйся memset
upd. Я временно стирал пост, поскольку выяснил, что неправильно вызывал memset (по памяти писал). Ща все правильно.
В дебаге же куча отладочной инфы, которая и замедлять должна, и куча вспомогательных структур, обилие которых, видимо, и вызвало overflow. Какой смысл, если релиз нормально работает?
Вывод: инициализировать статические переменные не особо-то и надо
Да не, дебаг-то может и не надо собирать, но вот 800Мб массивчик — это сила.
Просто float [10000][10000][2] должен выделять непрерывный кусок памяти и при этом он не расчисщает память, если такого куска нет и происходит какая-нибудь фигня, которую можно не сразу заметить.
А std::vector тоже выделяет непрерывный кусок, но при этом может расчищать себе место.
float [10000][10000][2] должен выделять непрерывный кусок памятиНа 64 битной системе — это не проблема
Какой смысл, если релиз нормально работает?Смысл в том, что релиз может делать вид, что он нормально работает, а работать неправильно. Затем и нужен дебаг. Например:
int main(int argc, char* argv[])
{
float f[10000][10000][2];
for(int i = 0; i < 10000; ++i)
for(int j = 0; j < 10000; ++j)
for(int k = 0; k < 2; ++k)
f[i][j][k] = 0;
return 0;
}
у меня в релизе работает, а
int main(int argc, char* argv[])
{
float f[10000][10000][2];
for(int i = 0; i < 10000; ++i)
for(int j = 0; j < 10000; ++j)
for(int k = 0; k < 2; ++k)
f[i][j][k] = 0;
std::cout << f[3434][343][1];
system("pause");
return 0;
}
уже нет.
А! Так ты еще и на стеке выделяешь. Это ж наркоманство!
На 64 битной системе — это не проблемаДа, похоже у топикстартера такая.
stack overflow
Вывод: инициализировать статические переменные не особо-то и надоСобственно, я так и заметил разницу во времени исполнения - понял, что инициализировать не надо, закомментил и обнаружил жуткую задержку. Вот и интересно стало - откуда она?
krasin, ты не мог бы объяснить время, которое выводит утилита time?
, ну, как бы, выделило-не выделило - работающие циклы (и при этом нормально завершающиеся, кстати) ведь от этого меньше или больше не становятся? Они абсолютно же одинаковые.
upd.: минуснул не я.
krasin, ты не мог бы объяснить время, которое выводит утилита time?Смотри на real. user и system тебя мало трогают (это детализация и она ваще тебя не парит)
Да пофиг. Глобал и правда инициализируется нормально.
В любом случае, вопрос не в том, как оптимизировать инициализацию, хотя за совет с мемсетом спасибо - в дальнейшейм буду использовать.
Может, тормозит твой профайлер? Еще я билдил g++, а не VS 2008. Тоже возможны варианты.
Тачка вроде не самая крутая, разве что памяти заведомо хватает (мб у тебя со swap борьба идет?)
Работает 50 и 10 секунд. Профайлеру я весьма доверяю.
Еще я билдил g++, а не VS 2008.У меня VS 2008 за пару секунд заполняет. Профайлера нет.
Дело в том, что у меня бюджетнейшая тачка-неттоп с интел-атомом, поэтому, видимо, так долго. Что до памяти, то её тоже маловато, но причин для того, чтобы банальная инициализация нулями исполнялась в 40 раз дольше, чем заполнение массива вместе с громоздкими вычислениями - всё равно не видно, даже если там и правда постоянно со свопом идет работа.
С инициализацией соотношение сил примерно такое же, но ненамного больше (Release — 3843).
На gcc примерно как и у тебя, только там тачка послабее, там 10 и 12 секунд.
Замени умножение сложением (т.е. приведи к одномерному массиву и индекс вычисляй как сумму предыдущего индекса + смещение)
перепутанные индексы могут основательно замедлить всё дело.
тачка-неттоп с интел-атомома! я, кстати, скорее всего понял
Atom — это in-order processor, да еще и кеша мало. Поэтому как тебе правильно сказал Blind, проблема в перепутанных индексах.
Ну там все так и делается. Я даже пробовал индексы [2] на начало вынести, это слабо сказалось.
Так, что у меня всё неоптимально и медленно - это я уже понял, но в чем разница между вычисляющим циклом и инициализирующим?
в чем разница между вычисляющим циклом и инициализирующима ты можешь посмотреть на количество cache miss-ов?
Если объяснишь, как это сделать - я как бы с профилировщиками вообще-то не работал, у меня простейшая функция, возвращающая время и всё.
MSDN что-то рассказывает про это. Тебя интересует L2 Cache Read Misses
Хотя это ща не самая главная гипотеза.
Кажется, там для этих цпу-счетчиков нужно использовать Profile Explorer, который есть только в Team Suite, а у меня Professional. Может быть какой-нибудь сторонний профилировщик посоветуешь?
Дело в том, что у меня бюджетнейшая тачка-неттоп с интел-атомом, поэтому, видимо, так долго. Что до памяти, то её тоже маловатоОднако массив на 800 миллионов байт помещается?
Ничего, кроме AQTime под винду не знаю, к сожалению.
И казалось бы, самым долгим и ресурсоемким должен быть второй цикл, поскольку там всякие умножения, никак не оптимизировать, но на деле оба цикла вместе выполняются 50 секунд, а если закомментить инициализацию программа работает 10 секунд.Не видя код, который генерирует MSVC, можно только строить предположения. Можешь запостить сгенерированный ассемблер?
В чем дело?
Ну и на будущее, в данном случае компилятор мог бы вообще выкинуть все циклы, потому что u нигде не используется. Лучше, как уже замечали в треде, печатать хотя бы один элемент из него.
edit: ну и замерить, сколько работает программа с инициализацией, но без вычислений, тоже было бы неплохо. Т.е. верно ли, что инициализация работает 40 секунд?
Не видя код, который генерирует MSVC, можно только строить предположения. Можешь запостить сгенерированный ассемблер?С включённой оптимизацией, превращается в
mov ecx, 200000000 ; 0bebc200H+сохранение edi в стеке
xor eax, eax
mov edi, OFFSET _f
rep stosd
Ну это не тот исходник, у автора заполняется не весь массивчик, а только внутренность за исключением границы.
GCC отучили использовать rep, но пока не везде. А именно, strcmp и memcmp GCC до сих пор делает через rep cmpsb, что медленнее вызова библиотечной функции.
Вопрос топикстартера относился именно к заполнению нулями всего двумерного массива, смотри внимательнее
Не понимаю. В первом сообщении написано "кроме границы", в коде написано кроме границы.
А, да, я ступил (перепутал границу и внутренность). Сорри. Но это не сильно влияет, у студии будет rep stosd в цикле.
А как смотреть сгенерированный мсвс код? Пока что просто выдрал из иды нужные куски
Сейчас сам смотрю код - какие-то там косяки с индексами мутные... Даже ида подсвечивает красным.
это точно release версия? что за call'ы во внутренних циклах?
Еще мне кажется, что это асм уже другого исходника, где как минимум переименованы функции, так что сложновато будет понять
В общем это только код main, тут только вызовы calc и trueCalc.
Удивляет странная sub_401450. Такое ощущение, что она вызывает функцию с заданными параметрами, потому что перед ней пушится адрес функции. Что-то я такого раньше не видел
я вставил cout'ы и только их, чтобы легче найти нужные места. Также, забыл написать, что в инициализирующим цикле, на самом деле, запись происходит дважды u[x][y][0] и для u[x][y][1] - вторую запись я закомментил. Ещё в коде по другому определены дефайны Nx = 10000, Ny = 7000, n = 100. Но как бы на код особо влиять не должно же.
Во-первых, idb к IDA:
http://slil.ru/28977823
Это релиз сборка, лично мой явный вызов этой функции в коде отсутствует. Зачем она нужна - не знаю.
Я просто вставил строки вида cout << "lol1";
Ааа, ну да! Адрес cout'а пушится для загадочной функции, а она и выводит текст, в свою очередь. Ну да, всё ок по этой части.
Не, ну cout'ы внутри "вложенности" могли поломать оптимизацию.
http://slil.ru/28978034
В архиве idb + новый асм-код тех циклов. Убрал вложенные cout'ы, вернул 10000х10000.
И ещё, большое спасибо всем, кто участвовал и участвует в разборе этой геморройной странности и помогал советами!
В архиве idb + новый асм-код тех циклов. Убрал вложенные cout'ы, вернул 10000х10000.
И ещё, большое спасибо всем, кто участвовал и участвует в разборе этой геморройной странности и помогал советами!
Слушай, ты точно пробовал запустить без отладчика и всяких измерений времени? Все-таки 10 секунд от 50ти можно отличить и так, по ощущениям
Ответить по вопросу треда мне нечего =)
Кстати прикольно, что переносить "дырки" в начало массива как раз не надо, потому что новые значения слоя зависят от близких ячеек в предыдущий момент времени.
http://slil.ru/28978034есть такой сервис, pastebin.com. Намного удобнее для задач выложить кусок кода, нежели slil.ru
Интересно, кстати, что Intel Complier скажет. Ни у кого нету под рукой?
, спасибо! Добавил в ссылки!
И ещё один вброс: если закомментить вычисления и оставить только инициализацию, то длится она 19 секунд. То есть реально дольше вычислений.
кстати, буквально вчера пробовал ставить. Скачал редистрибьютивс библиотека, установил, но что-то нужная кнопочка в мсвс не появилась. Так что на время пока забил.
И ещё один вброс: если закомментить вычисления и оставить только инициализацию, то длится она 19 секунд. То есть реально дольше вычислений.НО заметно меньше, чем <Вычисления + инициализация> - <Вычисления>?
А как смотреть сгенерированный мсвс код? Пока что просто выдрал из иды нужные кускиНеплохо изучать бинарники с помощью IDA, но прежде чем это делать, следовало посмотреть на список параметров компилятора cl.exe и заметить там ключик /Fa
Ок, в следующий раз буду замечать - никогда просто эти опции не читал.
Спасибо за подсказку, кстати!
Оставить комментарий
lujant
Есть глобальный массив float [10000][10000][2], пишу на c++, компилирую в msvs 2008. Программа решает уравнение Лапласа, вычисляя каждый элемент этого массива. В самом начале программы есть двойной цикл, который заполняет всю область массива, кроме границ, нулями: u[k][l][0]=0;. А потом есть ещё второй двойной цикл, который присваивает элементу значение, согласно поставленной задачи: uglobal[k][l][1-cL]=uglobal[k-1][l][cL]+uglobal[k+1][l][cL] + otn*(uglobal[k][l+1][cL]+uglobal[k][l-1][cL]);И казалось бы, самым долгим и ресурсоемким должен быть второй цикл, поскольку там всякие умножения, никак не оптимизировать, но на деле оба цикла вместе выполняются 50 секунд, а если закомментить инициализацию программа работает 10 секунд.
В чем дело?