[VS2005, STL] Это недоработка STL, или так и задумано?
В MSVS 6.0 передача параметра в двухаргументный ресайз идет по константной ссылке, что уже лучше. Так что это бага 2005-й студии. Щас проверил — в 2008 она осталась.
Однако неиспользование placement new это в данном случае ИМХО не есть гут

Это не баг 2005-й и 2008-й студии... Это баг 6-й студии

В стандарте написано, что элемент передается по значению (но почему?

Это баг 6-й студииа чего, кто-то пользуется в 6-ой студии микрософтовским STL?
если уж приходится сидеть на VS6, то только c STLPort, конечно
P.S. вот ещё про STL в VS6: http://support.microsoft.com/kb/813810
на предмет быстродействияКажется, что на предмет быстродействия приведённый код не влияет.
Кстати, судя по всему там в любом случае вызывается placement new, но используется copy-constructor. Так что, если в структуру добавить пустой copy-constructor, то время ресайза уменьшается на 7 порядков.
, спасибо за ссылку
Я пользуюсь. Проблем не имел. Правда, std::string я не использую обычно.
что используешь?
Слабо понял, что ты написал. Там по коду на создание N объектов один раз передаётся параметр по значению. Как это может влиять на порядок производительности?
Как это может влиять на порядок производительности?Иногда бывают ситуации, когда например структура просто добавляется для группировки некоторых данных. Она не является настолько самостоятельной сущностью, что бы ее создание могло быть определено в конструкторе (собственно поэтому это не класс, а структура). Например программа работает с какими-то данными в которых в числе прочего имеется массив неких элементов... Ну например адресная книга — есть массив адресов домов, массив людей с указанием ФИО, паспортных данных, номеров адресов в таблице домов, номеров квартир, ну и кроме того есть некая шапка, типа даты обновления базы. разумно одного человека описать структурой. Теперь у нас есть задача считать такой файл с диска. Было бы удобно выделить память под один дополнительный элемент такой структуры, но НЕ ИНИЦИАЛИЗИРОВАТЬ его автоматом, а просто получить на него ссылку и заполнить его по ходу дела. В случае когда интерфейс ресайза имеет вид
void resize(size_type _Newsize, _Ty _Val)
мы вынуждены делать минимум одно лишнее копирование на каждый объект — либо создавать пустой объект вызовом дефолтного конструктора (можно затраты на эту операцию сделать нулевыми) и затем копировать этот мусор в каждую из созданных ячеек массива. Либо создавать сразу нормальный заполненный объект и передавать уже его в ресайз (хотя в этом случае разумеецца стоит воспользовацца push_back — там по крайней мере передача объекта идет по ссылке). Как видим и там и там есть одно лишнее копирование.
Я уж не говорю про передачу объекта по значению — вапче не понимаю зачем это

У меня задача была немного другая и ее было возможно реализовать без потерь скорости и при такой реализации библиотеки (что я и сделал просто заинтересовала теоретическая подоплека ситуации. Зачем в STL сделали именно такой интерфейс контейнеров. Для общности, для простоты реализации, для предотвращения дублирования кода в самой библиотеке... ?

если в структуру добавить пустой copy-constructorТо ничего не будет работать, потому что внутри вектор тоже работает через конструкторы копирования.
Было бы удобно выделить память под один дополнительный элемент такой структуры, но НЕ ИНИЦИАЛИЗИРОВАТЬ его автоматом, а просто получить на него ссылку и заполнить его по ходу дела.Наверное, я плохо понимаю твои формулировки. Ссылка на память под класс без вызова какого-либо конструктора этого класса бесполезна и опасна. Поэтому структуры данных в Си++ не хранят несконструированных объектов.
Если тебя не устраивает скорость размещения памяти, можно воспользоваться аллокаторами или операторами new/delete. Если не устраивает скорость копирования, нужно хранить в векторе указатели вместо объектов. (Хотя в твоей конкретной задаче затраты на ввод-вывод перекроют затраты на работу процессора.)
вынуждены делать минимум одно лишнее копирование на каждый объект — либо создавать пустой объект вызовом дефолтного конструктораЯ не понимаю, почему одно копирование на каждый объект? Ты хочешь делать v.resize(v.size+1) для каждой новой структуры? Это вполне можно амортизировать, чем push_back и занимается. Или ты ведёшь речь о проблемах конструирования объектов с помощью конструктора копирования вместо конструкторов по умолчанию? Для твоей структуры производительность не падает до секунды.
10 элементовЭто, опять же, весьма странные цифры. Они наверняка получены в дебаге без включённой оптимизации inline. Когда работаешь с STL надо иметь ввиду, что debug и release билды могут различаться по производительности во много раз. Чтобы этого избежать, можно включить опцию /Ob2, хотя это может затруднить отладку, если ты любишь шаблонное программирование.
Timer 0: Total time=6.71812e-006 sec (reserve)
Timer 1: Total time=7.7889e-007 sec (resize)
10000000 элементов
Timer 2: Total time=1.81819e-005 sec (reserve)
Timer 3: Total time= 0.826397 sec (resize)
Ссылка на память под класс без вызова какого-либо конструктора этого класса бесполезна и опасна. Поэтому структуры данных в Си++ не хранят несконструированных объектовЭто ты сейчас говоришь не о структурах, а о классах. С точки зрения языка структура и класс — это почти одно и то же, однако с точки зрения "рекомендованного" использования они различаются очень сильно. Вот цитата из Страуструпа:
Я думаю о таких классах, как о "не совсем типах, являющихся просто структурами данных". Конструкторы и функции доступа могут быть весьма полезны даже для таких структур, но скорее для удобства, а не как гаранты свойств типа.
так что пустой конструктор по умолчанию для структуры очень даже возможен в реальной ситуации, а вот копирующий конструктор лучше бы вообще оставить предлагаемый компилятором по-умолчанию. И не придирайся к примеру с пустым копирующим конструктором — я же не говорю, что хотел использовать его в жизни. Это был только эксперимент(!) с целью быстро понять что используется внутри библиотеки — копирующий placement new или оператор присваивания. Все! Больше от этого эксперимента мне не нужно было ничего!
Я не понимаю, почему одно копирование на каждый объект? Ты хочешь делать v.resize(v.size+1) для каждой новой структуры? Это вполне можно амортизировать, чем push_back и занимается1. Сперва создаем объект структуры (затраты по времени = 0)
2. Заполняем эту структуру (1-е копирование)
3. Вызываем push_back, который с помощью placement new помещает объект в вектор (2-е копирование)
Если использовать v.resize(v.size+1, NewObject) - это еще одно дополнительное копирование (2-е которое выполняется в момент передачи аргумента (как раз то, чего я совсем не понимаю, зачем нужно было так делать)
А если использовать
v.resize(v.size+1)
v.back = NewObject
то тут уже будет три (!) дополнительных (!) копирования: 1-е — передача объекта, созданного дефолтным конструктором в двухаргументный ресайз, 2-е — копирование того же объекта в конец вектора и 3-е — копирование правильного объекта в конец вектора.
Поэтому я и сказал, что при таком интерфейсе мы имеем как минимум одно лишнее копирование на каждый объект. А при неправильном испоьзовании количество лишних копирований может быть и 3 на каждый объект
Это, опять же, весьма странные цифры. Они наверняка получены в дебаге без включённой оптимизации inlineВсе эти цифры были получены разумеется в релизе

так что пустой конструктор по умолчанию для структуры очень даже возможен в реальной ситуации, а вот копирующий конструктор лучше бы вообще оставить предлагаемый компилятором по-умолчанию
В реальной ситуации конструктор по умолчанию возможен как для класса, так и для стуктуры. Я не вижу, почему конструктор копирования класса, созданного конструктором по умолчанию, должен работать намного медленнее конструктора по умолчанию.
А если использоватьА если всё-таки не использовать ничего кривого? Вызов push_back - это одно копирование. Если объект пустой, то грамотное Си++ решение отработает с почти одинаковой скоростью как в случае конструктора копирования, так и в случае конструктора по умолчанию.
v.resize(v.size+1)
v.back = NewObject
то тут уже будет три (!) дополнительных (!) копирования: 1-е — передача объекта, созданного дефолтным конструктором в двухаргументный ресайз, 2-е — копирование того же объекта в конец вектора и 3-е — копирование правильного объекта в конец вектора.
Поэтому я и сказал, что при таком интерфейсе мы имеем как минимум одно лишнее копирование на каждый объект. А при неправильном испоьзовании количество лишних копирований может быть и 3 на каждый объект
Все эти цифры были получены разумеется в релизе
У меня в релизе resize на 10кк без reserve работает 375 миллисекунд. У меня один камень на 2.5ГГц. Думаю, что тебе стоит пересмотреть твой тест.
В реальной ситуации конструктор по умолчанию возможен как для класса, так и для стуктуры. Я не вижу, почему конструктор копирования класса, созданного конструктором по умолчанию, должен работать намного медленнее конструктора по умолчанию.Допустим есть такой код:
void f(int Arg)
{
int a;
int b = Arg;
..........
}
Чему будет равно значение a и b? Вот именно... a — неопределено, b — равно Arg.
Структуры по сути являются такими же переменными. Они в отличие от классов не обязаны быть всегда и везде полностью корректно инициализированными. И если я напишу
MyStruct ms;
То я могу ожидать, что ms пока что содержит мусор и ее еще надо определить. Именно про это и пишет Страуструп. И собственно именно так и происходит, если не писать самому конструктор по-умолчанию. А конструктор, предлагаемый компилятором в моем случае не делает вообще ничего, а значит и временнЫе затраты на его выполнение отсутствуют. С другой стороны копирующий конструктор должен создать копию объекта, и copy-constructor, предлагаемый компилятором (опять-таки в моем случае) просто делает банальное копирование бит-в-бит, так что он будет по-любому медленнее дефолтного конструктора
Если объект пустой, то грамотное Си++ решение отработает с почти одинаковой скоростью как в случае конструктора копирования, так и в случае конструктора по умолчаниюВ C++ объект (структуры, типа моей) не может быть пустым — он либо содержит мусор, либо нормальные данные и компилятор не будет продираца через цепочу вызовов и пытацца определить, что там передается. Про возможные оптимизации, типа оборачивания структуры в smart_ptr и пр. я сейчас не говорю, я просто хотел сказать, что на мой взгляд можно было достаточно легким изменением интерфейса контейнеров позволить значительно увеличить эффективность в некоторых задачах без особых затрат со стороны программеров-пользователей библиотеки.
У меня в релизе resize на 10кк без reserve работает 375 миллисекунд375 миллисекунд = 0.375 сек
у меня — 0.826 сек (проц — AMD Sempron 2ГГц)
Так что вопчем-то отличие не сильное. Просто однопараметрный ресайз при грамотной реализации должен тратить на порядки меньше времени. Например для реализации массивов в MFC/ATL мой тест показал значение в 40 наносекунд! А это уже просто на уровне шумов — точность таймера такие величины не позволяет измерять надежно.
я просто хотел сказать, что на мой взгляд можно было достаточно легким изменением интерфейса контейнеров позволить значительно увеличить эффективность в некоторых задачах без особых затрат со стороны программеров-пользователей библиотеки.Эффективность достигается путём правильного использования всех механизмов языка программирования. И только тогда, когда это нужно. Я согласен, что многие моменты в stl/boost остаются спорными: производительность очень часто приносится в жертву универсальности (смотрим, например, на shared_ptr). В реализации ATL/MFC коллекций тоже не всё гладко, потому что у них линейная амортизация по размещению памяти.
375 миллисекунд = 0.375 секНу да, не сильное, всего в три раза при 25% прибавке скорости работы ядра. Это меня, мягко говоря, смущает.
у меня — 0.826 сек (проц — AMD Sempron 2ГГц)
Так что вопчем-то отличие не сильное.
В реализации ATL/MFC коллекций тоже не всё гладкоУ самого есть претензии

Ну да, не сильное, всего в три раза при 25% прибавке скорости работы ядра. Это меня, мягко говоря, смущает.
Ну, надо проверять скорость работы памяти на каждом конкретном компе, компиляторы, настройки и пр... на суть проблемы это все равно не влияет — 3 раза, это не несколько порядков
resize, вызванный с одним параметром вызывает дефолтный конструктор и получившийся объект передает в другой resize по значениюНу, и для истории....![]()
нашел вот такую ссылку: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html...
Этa фича была добавлена в стандарт умышленно, с объяснением типа "для безопасной передачи в функцию элементов, уже записанных в массиве, типа такого:"
v.resize(v.size + 1, v[0]);
Как я понял добавление этого правила явилось прямым следствием пункта 5 параграфа 23.2.4.2 стандарта:
Notes: Reallocation invalidates all the references, pointers, and iterators referring to the elements in the sequence. It is guaranteed that no reallocation takes place during insertions that happen after a call to reserve until the time when an insertion would make the size of the vector greater than the size specified in the most recent call to reserve.
Однако непосредственно в момент ресайза старый блок данных еще существует, а значит и передача по ссылке вполне возможна.
В настоящее время проблема с передачей аргумента по значению в resize признана багом. Она занесена в список известных багов под номером 679 и имеет статус CD1:
CD1 - (Committee Draft 2008) - The full WG21 committee has voted to accept the Defect Report's Proposed Resolution into the Fall 2008 Committee Draft.Заглянул в драфт стандарта C++ от мая 2007 года. Вот какие изменения там приняты в отношении ресайза:
1. Добавилось объявление ресайза с одним аргументом.
2. Двухаргументный ресайз по-прежнему принимает второй аргумент по-значению.
3. Для одноаргументного ресайза явно указано, что будет вызываться конструктор по-умолчанию (что вопчем-то и сейчас происходит но при этом ничего не сказано про наличие/отсутствие копирующего конструктора, в то время как для двухаргументного ресайза явно написано, что копи-конструктор не должен бросать исключения. Это дает повод надеяться, что реализация одноаргументного ресайза будет пользовать только конструктор по-умолчанию, а значит в приведенном выше случае затраты на ее вызов будут составлять O(1).
Вот текст из драфта стандарта:
void resize(size_type sz);
9 Effects: If sz < size equivalent to erase(begin + sz, end;. If size < sz, appends sz - size
default constructed elements to the sequence.
10 Requires: T shall be DefaultConstructible.
void resize(size_type sz, T c = T;
11 Effects:
if (sz > size
insert(end sz-size c);
else if (sz < size
erase(begin+sz, end;
else
; // do nothing
12 Requires: If value_type has a move constructor, that constructor shall not throw any exceptions.
Что ж.. подождем

Оставить комментарий
4223080
Полез тут в потроха STL (std::vector) на предмет быстродействия и увидел вот такой код:Т.е. resize, вызванный с одним параметром вызывает дефолтный конструктор и получившийся объект передает в другой resize по значению
Подумал, что меня глючит и решил провести тест
Время замерял командой rdtsc
В качестве элементов использовал вот такую структуру:
Для теста выполнял 3 команды — создавал объект, делал reserve, потом resize
Получил следующие результаты:
Когда-то помницца копался в MFC... как щас помню там применялся placement new, а поскольку у меня дефолтный конструктор пустой, то и времени этот new отнимать не будет. Для проверки попробовал провести аналогичный тест для CAtlArray:
Как и предполагалось ресайз у CAtlArray реализован нормально, в то время как STL-вектор ресайзицца почти целую секунду. Как с этим бороцца?