- 01
- 02
- 03
- 04
- 05
- 06
- 07
- 08
- 09
- 10
- 11
- 12
std::vector<double> WBuffer;
std::vector<double> CleanWBuffer;
std::vector<Color> PixelBuffer;
std::vector<Color> CleanPixelBuffer;
void Scene3D::ClearBuffers()
{
const size_t n = static_cast<size_t>(ScreenSize[0] * ScreenSize[1]);
memcpy(&*(WBuffer.begin()), &*(CleanWBuffer.begin()), n * sizeof(*(WBuffer.begin())));
memcpy(&*(PixelBuffer.begin()), &*(CleanPixelBuffer.begin()), n * sizeof(*(PixelBuffer.begin())));
}
Быстрая очистка буферов.
CleanWBuffer предварительно заполнен 0.0, CleanPixelBuffer предварительно заполнен нужным цветом.
Можно было воспользоваться std::fill, но оно работает в несколько раз дольше.
Пришлось так вот лезть в потроха std::vector. Доставляют подряд идущие & и *.
burdakovd 12.11.2010 15:24 # 0
Иначе так было бы нельзя делать.
rat4 12.11.2010 15:33 # 0
Да и не понятна причина использования здесь вектора...
burdakovd 12.11.2010 15:47 # +1
Статический массив статичен, неизвестно какого размера делать.
Динамическую память выделять через new - потом неясно как размер менять если понадобится, лишний delete в деструкторе не забывать..., и вообще, мы в С++, на дворе 21 век, космические корабли...
absolut 12.11.2010 15:49 # 0
burdakovd 12.11.2010 15:54 # 0
Просто вообще говоря не пользовался ни malloc ни new[]. Предпочитаю работать с памятью через толстый слой абстракций типа векторов.
rat4 12.11.2010 16:37 # 0
Выделить новый буфер при необходимости - да это проблема.
> лишний delete в деструкторе не забывать...
Smart Pointers позволяют забыть
bugmenot 15.11.2010 18:22 # +3
absolut 12.11.2010 15:46 # +1
> Доставляют подряд идущие & и *.
Это потому что memcpy() хочет адрес нормальный, а не итератор.
> sizeof(*(WBuffer.begin())));
нагляднее sizeof( WBuffer::value_type );
ch 12.11.2010 18:09 # −2
??
Govnoeb 13.11.2010 14:51 # +1
Color default_color = { ... };
PixelBuffer.assign(n, default_color);
burdakovd 13.11.2010 17:24 # 0
В случае когда количество элементов не меняется, assign и fill эквивалентны.
Конструирование нового вектора (как предлагает ch) подозреваю помимо этого ещё и привело бы к выделению новой области памяти вместо того, чтобы просто очистить использованную ранее, хотя насколько это повлияло бы не знаю.
Объединяет все эти три метода то, что внутри STL будет проходить цикл, который будет заполнять вектор нужными значениями (0.0). Этот цикл довольно таки долог. Именно этого я и избежал используя memcpy. Как теперь выяснилось, учитывая специфику представления double в двоичном виде, можно использовать memset.
Dummy00001 12.11.2010 20:44 # 0
Это официальная фича std::vector: указатель на первый элемент есть указатель на непрерывный массив содержащий все элементы вектора.
Говно, только если например тип Color не есть POD (plain old data). Если это просто структура, то тогда все в порядке.
> Можно было воспользоваться std::fill, но оно работает в несколько раз дольше.
Что меня неимоверно бесит. STL и темплейты в прошлом обещали что позволят компиляторам кучи новых оптимизаций. Две пятилетки спустя, а воз и ныне там.
Govnoeb 13.11.2010 14:52 # 0
Dummy00001 13.11.2010 14:58 # −1
ctm 12.11.2010 22:17 # 0
даже если так, может проще:
memset(&*(WBuffer.begin()), 0, n * sizeof(*(WBuffer.begin()))); // как раз 0 дабловский получится.
memset(&*(PixelBuffer.begin()), 0, n * sizeof(*(PixelBuffer.begin())));
тогда и 2-х лишних массивов не надо.
Можно пойти еще дальше - поэкспериментировать с написанием другой реализации memset - не через rep movsd - на каких-то системах может оказаться заметно быстрее, особенно если будет мало push и pop :)
PS. Не хватает проверки на нулевой размер вектора.
burdakovd 12.11.2010 22:40 # 0
А 10% на очистку буферов (утилитарная операция) это много.
А вот то что дабловский 0.0 это когда все байты равны 0 - это подозрительное предположение.
nil 13.11.2010 00:26 # 0
{
double a=0;
char *b=&a;
for(int i=0; i<sizeof(double);i++) printf("%d\n", b[i]);
}
absolut 13.11.2010 09:22 # 0
nil 13.11.2010 10:54 # 0
Вот навскидку ресурс: http://speleotrove.com/decimal/
absolut 13.11.2010 11:30 # −1
Есть ли гарантия, что на всех аппаратных платформах, под которые есть C++ компиляторы этот стандарт имеет место быть?
nil 13.11.2010 11:38 # +1
Нет, думаю, такой гарантии нет:) Хотя... Я C++ не знаю, но там в стандарте написано, что поддерживается тип double, нет? Сказано там что-то про реализацию этого типа? Если сказано, то в компиляторах, соответствующих стандарту, должно прокатить. Если реализация 754 верная. Если...
absolut 13.11.2010 12:11 # 0
nil 13.11.2010 12:26 # 0
Но изначально речь шла о том, как по-быстрому занулить дабл. Всеми нулями быстрее, чем нулями и одним 0x80:)
absolut 13.11.2010 13:05 # 0
burdakovd 13.11.2010 12:31 # −1
ch 13.11.2010 07:50 # 0
Объясните хоть чем это хуже 10-й строки
memcpy(&WBuffer[0], &CleanWBuffer[0], n * sizeof(WBuffer[0]));
Алгоритм для работы с памятью медленный? Непорядок! Процессор загружен во время очистки экрана? Вот черт, на рендер-то и времени глядишь не хватит!
burdakovd 13.11.2010 08:27 # 0
И как обычно в комментариях дали достаточное количество идей по его улучшению.
"Алгоритм для работы с памятью медленный? Непорядок! Процессор загружен во время очистки экрана? Вот черт, на рендер-то и времени глядишь не хватит!"
К чему сарказм?
Проект был учебный, запустил профилировщик. Увидел что 10% времени уходит на очистку [с помощью std::fill]. Оставшиеся 90% размазаны по коду рендера. Прежде чем лезть и оптимизировать [более сложный] код рендера, можно заменить std::fill на memcpy (про то что 0.0 == 0x00000000 я тогда не знал к сожалению, иначе обошёлся бы memset) и сэкономить 10%.
ch 13.11.2010 16:17 # 0
WBuffer = vector<double>(n, 0);
Не вижу оснований считать что fill быстрее без профилирования. Таким образом говно началось с момента применения неадекватных инструментов. Если есть пустая копия, то оптимизация заключается в смене аргумента
WBuffer = vector<double>(CleanWBuffer);
Если оптимизировать дальше, возникает вопрос зачем тут вектор, ведь memset'ом работаем как с массивом...
Сарказм к тому, что все много пишут, а самое правильное решение, находящееся в учебнике для начинающих, игнорируют и минусуют.
Вон, Govnoeb чуть больше манула прочитал.
burdakovd 13.11.2010 17:36 # +1
Почему самый обычный?
Использование конструктора - это самый обычный способ *создать* *новый* вектор заполненный нулями. Потому он и называется конструктором.
Нужно заполнить вектор нулями... переводим слово "заполнить" на английский... это будет fill... о, есть библиотечная функция std::fill!
Чем не обычно?
На ваше и Govnoeb-a решение ответил.
ctm 13.11.2010 09:01 # 0
посмотрите формат хранения даблов: http://en.wikipedia.org/wiki/Binary64
0 записывать так можно.
у меня была аналогичная проблема при написании проекта на дельфи 6, а там очень плохо с оптимизацией кода компилятором.
более того, собственная реализация аналога memset ускорило еще процентов на 30.
Если хотите, пришлю код.
burdakovd 13.11.2010 17:40 # +1
guest 16.12.2010 23:13 # 0
guest 16.12.2010 23:18 # 0
Например можно шаблонную специализуцию пихнуть для не стандартных дивайсов, в которой будет более медленная, но верная очистка.
andremacareno 13.11.2010 10:09 # +1
А в Си вроде была функция realloc() (для malloc()/calloc()) .
Govnoeb 13.11.2010 15:40 # 0
во2 может функции отрисовки подсововать разные массивы для отрисовки, чем занулять один?
burdakovd 13.11.2010 17:32 # 0
2) чем разные массивы лучше? занулять их всё равно придётся, но помимо этого придётся ещё и память много раз выделять.
Govnoeb 14.11.2010 12:38 # 0
AxisPod 15.11.2010 07:55 # 0
guest 16.12.2010 23:19 # 0