Pull to refresh

Comments 139

Думаю, в статье следовало бы сделать ремарку о том, что в DTrace используется свой D-язык, который не имеет ничего общего с другим языком D.

И еще вот это:
Топ хранится в std::list, где элементы — это указатели на объекты-биды. Один из коммитов привнес такой чудесный код для обрезания топа: list.resize(max_results). Как вы уже догадались, list.resize не вызывает delete на элементы-указатели. Нужно пройтись и ручками освободить память всех лишних указателей до вызова resize.
У вас что, реально был list в котором лежали просто голые указатели? Даже не list unique_ptr-ов?
Вообще, сам по себе лист (или массив) голых указателей — не преступление.
Проблема наступает когда не учитывается ownership объектов, куда они указывают (тут можно порекламировать Rust, но не будем). Нужно решить кто является овнером (на всё время жизни) и воткнуть туда unique_ptr, но если вообще ничего не понятно, и указатели прилетают то слева то справа, то можно обложить всё shared_ptr-ами, хотя тогда становится не ясен профит от использования плюсов, в таких случаях лучше уползти на java/c# ))
Преступление-преступление. Если этот лист — единственный, кто хранит эти указатели, то это должно быть явно выражено в коде — использованием unique_ptr. Если есть и другие пользователи этих указателей — shared_ptr. И профит по сравнению с java/c# всё-же есть, поскольку shared_ptr гарантирует немедленное удаление объекта при освобождении указателя всеми владельцами, а java/c# ничего не гарантируют.

А лист голых указателей значит — «я тут написал код, который понимаю только я и только сейчас, модель владения объектами есть только у меня в голове и никому я её не расскажу».
> И профит по сравнению с java/c# всё-же есть, поскольку shared_ptr гарантирует немедленное удаление объекта при освобождении указателя всеми владельцами, а java/c# ничего не гарантируют.

В этом у манагед-языков как раз и профит. На плюсах мы будем постоянно ковырять общую кучу, что не так быстро. (либо придётся озаботиться арена-аллокаторами), а ГЦ потом просто соберёт весь этот хлам скопом, что обычно шустрее.
На плюсах мы будем постоянно ковырять общую кучу, что не так быстро.
Есть подозрение, что у вас довольно старые представления о современных механизмах распределения памяти.
UFO just landed and posted this here
Вообще, сам по себе лист (или массив) голых указателей — не преступление.
Вообще-то есть принципиальная разница между std::list с голыми указателями и std::vector с голыми указателями. Сложно придумать настолько же неэффективную структуру данных, как std::list для голых указателей. Сценарии, когда такое может потребоваться можно придумать (например, требуется сохранять валидность итераторов на элементы списка при модификации списка), но это очень уж редкие сценарии именно в сочетании с хранением голых указателей как элементов списка.
Кстати, недавно в boost добавили полезную либу — Boost PolyCollection. Предназначена для хранения полиморфных объектов, хранящий в себе указатели на объекты базового класса. По тестам более эффективный нежели vector<unique_ptr>.
Подробности тут:
bannalia.blogspot.com/2014/05/fast-polymorphic-collections.html
www.boost.org/doc/libs/1_67_0/doc/html/poly_collection.html
Использование std::list в этом месте месте обусловлено, в первую очередь, необходимостью вызова sort. Поделитесь более эффективным способом отсортировать большой массив структур более 10К раз в секунду?
Так у вас list голых указателей на структуры или list структур, внутри которых есть указатели?
Конечно list голых указателей. Иначе бы на resize вызывались деструкторы структур и утечки бы не было. Но это непозволительная роскошь, двигать структуры при сортировке.
Ну и насколько, по вашим замерам, сортировка list-а голых указателей эффективнее сортировки vector-а или deque голых указателей?
Согласен, list примерно на 20% медленнее vector-а на сортировках, а у нас там много чего еще (например, операций удаления из середины некоторых бидов), поэтому по совокупности list выигрывает в нашей задаче.
В вашем бенчмарке еще очень не хватает reserve для vector-а перед заполнением. Ну а как себя показывает deque в сравнении с list-ом в совокупности (на сортировке он же выигрывает)?
reserve накинул еще пару процентов разрыва:

Run on (1 X 3491.99 MHz CPU )
2018-04-27 11:49:10
— Benchmark Time CPU Iterations
— BM_list_smart 14538337425 ns 14505242000 ns 1
BM_vector_smart 11492866976 ns 11462727000 ns 1
BM_deque_smart 12638374021 ns 12605011000 ns 1

Дек не пробовал. Вообще стоит, конечно, пересмотреть все используемые структуры, коду 7 лет в обед…

Сортировка много раз в секунду? Эм, heap на векторе же. Хотя стандартный priority_queue, конечно, совершенно никчемный (как обычно) и не позволяет динамически менять вес элементов. Но сама структура данных позволяет это делать за O(log N), что явно быстрее полной сортировки.

Ну вот тесты выше показали, что std::sort на векторе\списке — 20% потерь на списках. Но в совокупности с удалениями из середины (которых, в принципе можно вообще избежать, перестроив логику). В общем вопрос открытый.

Вектор обычно быстрее даже с учетом удаления из середины. Причем, быстрее на порядки. А вот необходимость сортировки, да еще и такой частой — как раз вопрос к структуре данных.

Вектор в сравнении со списком выигрывает лишь в одной операции — обращение к произвольному элементу, а проигрывает в двух: удалении\вставке произвольного элемента. В остальном они абсолютно идентичны (кроме ~20% в сортировках из-за оверхеда на указателях, используемых в реализации самого списка). Поэтому «быстрее на порядки» — не корректное заявление.
Сортировка необходима в аукционе, ответы приходят в произвольном порядке (каждый ответ содержит набор разных цен), и нужно либо сортировать при вставке (дорого), либо один раз в конце (наш вариант).

Я не хочу спорить с вашими "проигрывает", которые моими тестами не подтверждаются. Сортировать при вставке ничего не надо, если это вставка в heap (или изменение веса в нем же). Собственно, тут тоже спорить не о чем, если вы не понимаете, как и для чего работает heap, или же я не понял ваших задач.

Перечитал и понял, что вы скорее всего, недопоняли про «Сортировку много раз в секунду». Здесь имеется ввиду что на входящие 10К запросов происходит 10К независимых аукционов, в каждом из них сортировка (в списке) происходит один раз, в конце. Я в курсе что при вставке в кучу сортировка происходит автоматически, только это дороже чем сортировка один раз в конце.
А сколько вы потом отрезаете? С кучей есть один прикол, про который не стоит забывать: эта структура просто-таки создана для того, чтобы все префетчеры перестали работать. Так что если ваша куча влазит в L1 — то она реально очень быстрая и клёвая. А вот если нет — тогда вдруг происходит кратное замедление.

10K запросов в кучу пихать не нужно, а вот если вы возьмёте priority_queue и будет хранть 100 лучших результатов, которые вам реально нужны — результат будет совсем другой.
Только надо не забыть что при хранении 100 лучших результатов в пирамиде эту самую пирамиду нужно затачивать на поиск худшего результата, а не лучшего. Это совсем не очевидно…
Ну это же классическая задача для собеседования… Я думал об этом уж все точно знают…
UFO just landed and posted this here
list T* плох не потому что list, а потому что list T* вместо list T. Это, конечно, если по большому O и замерам он действительно обходит vertor.
Поделитесь более эффективным способом отсортировать большой массив структур более 10К раз в секунду?

Самый эффективный способ — не сортировать вовсе: попробуйте set/multiset. Можете также попробовать priority_queue
На абстрактном тесте на вставку\сортировку получается примерно так:

Run on (1 X 3491.99 MHz CPU )
2018-04-27 21:01:45
— Benchmark Time CPU Iterations
— BM_list_smart 14358524911 ns 14249840000 ns 1
BM_vector_smart 11455311425 ns 11442017000 ns 1
BM_set_smart 15602568549 ns 15579649000 ns 1
BM_pqueue_smart 12102934676 ns 12087128000 ns 1

Код — collabedit.com/4ex6s. Но, опять же, раньше там были вставки\удаления из произвольных позиций, с тех пор много чего поменялось, а «список» остался. Не знаю почему все кинулись оптимизировать этот код, было бы интереснее послушать про истории утечек в ЯВУ.
опять list unique_ptr'ов…
Но, опять же, раньше там были вставки\удаления из произвольных позиций, с тех пор много чего поменялось, а «список» остался

удаление из произвольной позиции list'а — O(N) поиск элемента и O(1) удаление. Удаление из произвольной позиции vector — O(1) поиск и O(N) удаление. У set поиск+удаление O(log(N)).
На голых вставках «вектор» быстрее «списка»:
BM_vector_smart_ins_rm 15327045208 ns 15315396000 ns 1
BM_list_smart_ins_rm 21855116654 ns 21838877000 ns 1

Но с циклом рандомных вставок\удалений уже начинает отставать:
BM_vector_smart_ins_rm 30417423216 ns 30374146000 ns 1
BM_list_smart_ins_rm 29354732199 ns 29316292000 ns 1

В итоге к финишу они приходят одновременно.

Если честно, я потерял нить спора. Кто кому что пытается доказать?

Я пытаюсь не доказать, а объяснить. ideone — тут наглядно видна разница между list указателей и list значений. Я, конечно, удивлен сильным отставанием set, но попробовать всё равно стоило. Тест вставок/удалений из случайных позиций для set не имеет смысла. А еще priority_queue уходит вперед vector/list/deque.

Выхлоп на моём скромном i7 8700K
list<unique_ptr<T>> 27598 ms
list<T> 24775 ms
vector 17061 ms
deque 17831 ms
set<unique_ptr<T>> 26980 ms
set<T> 26656 ms
priority_queue 15560 ms
list<unique_ptr<T>> ins/rm 20973 ms
list<T> ins/rm 16964 ms
vector ins/rm 14641 ms
finished

upd: проглядел, что вместо сравнения объектов по значению, тест в половине случаев сравнивает указатели на них. В итоге с корректным компаратором list T* резко проседает, а priority_queue сильнее вырывается вперед
Результат
list<unique_ptr<T>> 33105 ms
list<T> 25850 ms
vector 20933 ms
deque 22210 ms
set<unique_ptr<T>> 26863 ms
set<T> 26618 ms
priority_queue 17046 ms
list<unique_ptr<T>> ins/rm 22084 ms
list<T> ins/rm 17485 ms
vector ins/rm 15120 ms
finished

UFO just landed and posted this here

Вы сортируете, а потом берете только 5 первых значений? Это же очень алгоритмически неэффективно. Смотрите std::partial_sort (если порядок этих 5 элементов важен) либо std::nth_element если нет. Если исходный список большой, то может иметь смысл использовать std::priority_queue.

UFO just landed and posted this here
Разница между двумя указателями и одним сильно меньше, чем между одним указателем и нулём указателей.

сложно было без замеров сказать, будет ли там быстрее vector/set/priority_queue, а вот замена list T* на list T дает гарантированный прирост

Надо смотреть на все случаи использования конкретной структуры данных и профилировать, иначе это тычки пальцем в небо.

Так я ж сразу писал: «попробуйте то, попробуйте это...». В теории set должен был быть выгоднее, но там кеш миссы подкосили.

Почему это не сортировать вовсе? Просто сортировка размазывается по всем вставкам, и последовательный доступ очень неэффективный (с точки зрения кеша, например).

да не совсем. Вставка N элементов в сет — N log(N). Сортировка — тоже, но сортировка (судя по всему) там проводится периодически, через каждые M вставок/удалений где M << N. Плюс у автора, как я понял, структуры данных побольше и эффективно не мувятся.
Согласен, почему-то всегда считал, что все умные указатели дают значительный оверхед, на тестах это сейчас не подтвердилось. Буду переделывать. Но и за 7 лет проекта это первая проблема такого масштаба.
UFO just landed and posted this here
Ну, как правило, для чего конкретно нужны виртуальные деструкторы, вам вряд ли кто-то расскажет уже через неделю после пройденного собеседования. Да, я искренне считаю удаление класса-наследника через указатель на базовый класс «хитрой» штукой. Если не это, то что?
UFO just landed and posted this here
Как вы уже догадались, list.resize не вызывает delete на элементы-указатели.

Ну блин, как так можно? Плюнули на последние 10 лет прогресса С++ в плане управления памятью — вот вам и проблемы. Умные указатели вам в помощь.
Согласен с комментаторами выше — std::list без умных указателей — варварство какое-то (но вполне возможно, что у Вас просто не C++11. Тогда могли бы из того же Boost использовать).

Меня волнует другой вопрос — КАК у Вас получилось решать проблему утечку памяти не натолкнуться за эти 6 дней на санитайзеры? У меня по первой ссылке в поиске на SO есть реакомендация ASAN.
Честно говоря, термин «санитайзер» впервые встретил перед публикацией, случайно поискав тему утечек на хабре (clang-овский кто-то упоминал в комментах к какой-то статье). А гугловский нашел только что.
Расскажите насколько замедляется приложение при его использовании и какие есть подводные камни. Спасибо!
UFO just landed and posted this here

Вообще-то они все гугловские. Просто команда из google их реализовала для gcc и clang в свое время.

UFO just landed and posted this here

В C++ есть автоматическое управление памятью

UFO just landed and posted this here

Она явная, появляется, когда вы решите, что она вам нужна.


Как пример: std::shared_ptr/std::uniqure_ptr, placement new и move semantics

UFO just landed and posted this here

Ну, стандартные типы STL все довольно-таки безопасны. "Плоские" указатели в С++ — это не стандартная фича языка, а необходимое условие для совместимости с чистым Си.


не на контроллере а-ля stm32

C++ — язык для всего (в отличие от жабаскрипта, сисярпа, и жабы), поэтому навязывать прикладные подходы от настольного программирования не совсем правильно. К тому же С++ используется там, где традиционные подходы оказались непроизводительными и там действительно уже нужно явно контроллировать узкие места.


А вот про std::resize поверх std::list<T*> — это явный прокол, объясняющийся банальным непониманием работы стандартной библиотеки. Такие косяки допускаются в совершенно любом языке и их причина — не дизайн языка, а банальная невнимательность

UFO just landed and posted this here
что за такая странная любовь у мышей к пожиранию кактусов?

Это скорее стремление не городить велосипеды. C++ позволяет нативно сливаться в экстазе с Си, на котором написаны ядра операционных систем, системные библиотеки и прочие непотребности.


Писать любую обертку для "безопасного языка" — долго, сложно и неблагодарно. Кто хоть раз писал тысячу [DllImport] в дотнете, знает, как хочется просто за#includ'ить заголовочный файл нативной библиотеки

UFO just landed and posted this here
Это откровенный технический долг, за который платит клиент.

И С++ тут ни при чем, опять же. Утечку памяти можно сделать и в C#
Использование умного или намеренно тупого языка не освобождает от отвественности тестировать свой код


Безопасный язык и его приятный привкус.

Касаемо С++ стоит различать язык "до C++11" и после. То, что было раньше — треш и угар, хорошо, что тот "цэпэпэ" от нас почти ушел.
Современный C++11/14/17 по удобству очень похож на C#, разве что Linq нет и рефлексии (но рефлексией и в дотнете не так часто пользуются)

UFO just landed and posted this here
Делал сам, проверял. Но это надо сделать специально.

Указанный в статье способ — тоже специально.

Не хочется. Тем более, что почти всегда в нугете есть подходящий враппер, накрайняк pinvoke.net в помощь.

Ну в более новых языках типа раста есть вполне себе удобный bindgen. Кто-то там по-моему целый MySQL перегенерировал на расте. Не сказать, что безопасности написанного кода прибавилось, но вот работать уже с ним можно без извратов с «экстазом».
UFO just landed and posted this here
сам Роб Пайк, любитель всяких Си — сделал golang
Задолго до Golang-а, Роб Пайк сделал Limbo, в котором так же был GC.
В С++ программист всё ещё решает, нужно ли автоматическое управление память или нет.
Именно это и делает C++ до сих пор востребованным.
хотя существуют исследования, доказывающие, что автоматическое управление памятью даже в плане производительности бывает заметно лучше, чем ручное управление
Эти же исследования доказывают, что для достижения сравнимой производительности нужно иметь в четыре раза больше RAM, чем для такой же программы, но написанной на языке с ручным управлением памятью.
UFO just landed and posted this here

У меня на GCC в 400 килобайт поместилась полностью прошивка железки, в которой есть веб-сервер, telnet-консоль, драйверы периферии, поддержка файловых систем и вебморда с картинками (которая занимает половину из всего объема прошивки).


Так что про Hello World это громко.


Да, поддержка стандартной библиотеки неплохо раздувает код, но не настолько, как впиливание garbage collector в memory constrained платформу

UFO just landed and posted this here
Limbo широко использовался? Там что-то было принципиальное?
Это не имеет значение. В контексте вашего высказывания о том, что «сам Роб Пайк, любитель всяких Си — сделал golang» важен сам факт того, что будучи «любителем Си», Пайк задолго до Golang сделал язык со сборкой мусора. После чего зачислять его в «любители Си» несколько странно.
Когда ко мне (в мою контору) приходят люди со знанием С++ они искренне недоумевают, почему мне такие специалисты не нужны.
Чтобы говорить конкретно про вашу контору, нужно знать, что это за контора, чем она занимается и насколько успешно. Но здесь речь не об этом, а о том, что не смотря на то, что в большинстве случаев языки с GC успешно справляются, все равно есть ряд областей, где от GC больше вреда, чем пользы. И вот в таких областях чтобы отказаться от C++ и выбрать какую-нибудь Java, нужно иметь очень веские доводы (ну или альтернативный способ мышления). Насколько я слышал, он-лайн аукционы рекламы — как раз относятся к таким областям.
Да. И речь идёт про 250к+ памяти.
Это вы не смейтесь, речь про приложения, в которых потребление памяти измерялась сотнями мегабайт, а сейчас уже и десятками гигабайт.
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Quantifying the Performance of Garbage Collection vs. Explicit Memory Management, 2005-й год:
Comparing runtime, space consumption, and virtual memory footprints over a range of benchmarks, we show that the runtime performance of the best-performing garbage collector is competitive with explicit memory management when given enough memory. In particular, when garbage collection has five times as much memory as required, its runtime performance matches or slightly exceeds that of explicit memory management. However, garbage collection’s performance degrades substantially when it must use smaller heaps

Тут говорится, что приложение с GC будет показывать такую же или даже лучшую производительность, но ценой расхода дополнительной памяти. В частности, для достижения такой же производительности требуется в пять раз больше памяти.

На что ссылаетесь вы — хз. Впрочем, ереси вы здесь и так уже наговорили порядком.
UFO just landed and posted this here
Результаты бенчмарков по вашей ссылке где?
UFO just landed and posted this here
Полагаю, что для человека, который предпочитает не писать тесты, нормально давать ссылку сперва на MSDN, где никаких замеров нет. А потом ссылку на Benchmark Game (за упоминание которого в приличном обществе с человеком уже давно перестают разговаривать). Только меня это все больше убеждает в том, что у вас уж очень альтернативный стиль мышления.

Но вот что видно по расходу памяти по вашей же ссылке на Benchmark Game: Go потребляет меньше памяти в бенчмарках reverse-complement, fannkuch-redux, n-body, k-nucleotide. Все. Четыре случая из десяти.

На игрушечных примерах. Если ваши познания о накладных расходах на GC и ручное управление памятью базируются только на примерах из Benchmark Game, тогда ой. Спорить с вами невозможно.
UFO just landed and posted this here
Человек предлагает втыкать всюду проверки, где только можно.
Проверки не заменяют тестов. Но вы этого, очевидно, еще не понимаете.
И после этого вы мне предъявляете, что я не читаю комментарии?
Сможете дать точную цитату где я что-то подобное вам «предъявлял»?
Вы просили ссылку — я вам привёл ссылку. Где ваши ссылки?
Актитесь, любезный, я вам дал ссылку на серьезное исследование, а не на маркетинговый булшит.
UFO just landed and posted this here
Проверки заменяют тест.
От многократного повторения чепуха быть чепухой не перестанет.
Более того, тесты не проверяют логику.
Не могу понять логику того, кто написал сей шедевр.
Тестер напишет тест так, как ему объяснил программист.
Во-первых, не «тестер», а «тестировщик». Во-вторых, если у вас тестировщик пишет тесты со слов программиста, то с тестированием у вас такие же проблемы, как и с логикой.
Видимо, вы не писали с соблюдением контрактов.
Да куда уж нам.
Моя программа уже 3 года на подстанции 110 кВ работает и ни одного падения.
Аргумент, да.
Пожалуйста
Да уж, если это пример для «И после этого вы мне предъявляете, что я не читаю комментарии?», то мне остается только поинтересоваться «А с головой у вас все нормально?»
UFO just landed and posted this here
Я не доктор, но общаться с не совсем нормальными людьми, не умеющими в обычную логику, при этом считающими себя программистами, доводилось. И, боюсь, сейчас тот же самый случай. Поэтому не хочу лишний раз ходить по тем же граблям.

Вы, например, делаете допуск, что программист может получить неправильную спецификацию, может неправильно ее понять, могут быть неправильно написаны тесты. Но при этом вы не допускаете мысли о том, что с контрактами может быть все то же самое.

Это, в купе с другими вашими высказываниями, приводит меня к выводу, что конструктивного общения не получится из-за вашего альтернативного мышления. За сим прощаюсь. Жалею, что в очередной раз ввязался в разговор с вами.

Да, чтобы не оставлять повисших вопросов:
Покажите мне, где я написал «со слов программиста»?
Вот ваши слова: «Тестер напишет тест так, как ему объяснил программист.» Дословно «со слов программиста» в вашей фразе нет, но я ее понял именно так.
UFO just landed and posted this here
GC не требует в четыре раза больше памяти, чем через руками. Это кусок рантайма.

важно не то, как работает сборка мусора, а как работает аллокатор в языках с GC. И если бы вы это знали, вы бы прекрасно понимали почему GC требует больше памяти. Это во-первых. Во-вторых, прикрутить (скажем) джавовский аллокатор вместо стандартного в плюсах можно. И если б он давал столь существенный прирост производительности, его б использовали повсеместно. В-третьих, обычно тесты c++ vs java/c# либо синтетические, либо откровенно плохо написаны. «std::list vs java array» — такое я уже видел.
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
Из статьи видно два откровенных косяка в управлении разработкой: во-первых, не было должного тестирования софта перед его отправкой «в бой» (если бы было, то ситуация, когда в топ бидов попадает только часть всех найденых бидов, была бы проверена еще на тестах). Во-вторых, когда утечка памяти обнаружилась, люди вместо того, чтобы отследить после каких изменений она началась, вернуться на нормальную версию и далее в спокойной обстановке искать утечку, бросились изучать инструменты, с которыми никогда дела не имели.

Это наводит на мысли о не сильно высоком уровне квалификации. В таких условиях люди и на языке с GC наломают дров.
UFO just landed and posted this here
Лично я не вижу смысла писать тесты.
Спасибо за каминг аут столь откровенное признание. Пожалуй, на этом общение с вами можно завершить.
UFO just landed and posted this here
UFO just landed and posted this here
UFO just landed and posted this here
С типизацией тестов надо меньше, но типы это не панацея. По крайней мере в распространенных языках нет типа «простое число меньше 10000», хотя это может быть важной частью алгоритма, в частности в дотнетовской BCL это важная конатнта для реализации хэшсета.

Пред и постусловия это прекрасно, но они тоже не все ловят. Эмерджентность во все поля, то, что каждый компонент работает правильно не гарантирует совместной корректной работы.
UFO just landed and posted this here
«Простые чиселки» — это пример. Смысл писать полноценный тест на какой-нибудь контроллер веб-интерфейса просто чтобы показать что от них бывает польза я не вижу.

Что касается этой аргументации:
Если вы не читали всё обсуждение, я напишу ещё раз: С++ допустил утечку памяти. И не важно: были тесты, не было их… С++ допускает утечку памяти, а значит — память будет течь неизбежно. А тесты — либо написаны не были, либо тесты отработали кривую логику, кривого алгоритма.

Это из разряда «сгорел сарай — гори и хата» и «чего мыться — все равно через неделю испачкаюсь».
UFO just landed and posted this here
Покажите язык в котором невозможно организовать утечку памяти.
UFO just landed and posted this here
У вас был аргумент «допускает утечку памяти, а значит — память будет течь неизбежно». Так вот — он применим к любому языку кроме тех которыми никто не пользуется.

Изначальную порочность С++ вы еще не доказали.
UFO just landed and posted this here
А в Компонентном Паскале есть указатели или хотя бы динамические массивы? Если есть — значит, утечка там тоже возможна.
UFO just landed and posted this here
Я вполне понимаю его аргумент в том смысле, что есть языки/рантаймы, в которых, чтобы получить утечки памяти, доступ по некорректным адресам, нарушение структуры кучи и т.п. — надо использовать какие-то явно названные средства, так, что по одному упоминанию такого средства можно обнаружить факт (обычно это обозначается словом unsafe), а есть такие, в которых можно добиться безопасной работы, но это требует дисциплины, а опасные средства никак явно не маркируются.

И соответственно первые — это Java, .NET, тот же компонентный Паскаль и Оберон, если верить агитаторам за них в этом треде, а второй — C++, потому что в нём даже ряд стандартных интерфейсов используют указатели, и просто факт использования указателя никак не означает опасного использования (а значит — поиск факта некорректного использования значительно усложняется).

То, что коллега prospero78su не может это сформулировать корректным образом — вопрос его дискуссионного стиля, а не самого факта. Этот комментарий это хорошо подтверждает. И мне не нравится, что вместо понимания сути того, что он говорит, оппоненты лезут в бутылку (а кто-то явно даже карму минусует).
Этот комментарий это хорошо подтверждает.
Что именно он подтверждает? Что «коллега prospero78su» несёт чушь, рассуждая о вещах, о которых он понятия не имеет?

Ну посмотрите на этот ваш «великолепный» комментарий предложение за предложением.

Примерно так
В Компонентном Паскале — не бывает указателей, у которых можно свернуть башку.
Обычые указатели там, несоменного бывают. А чем «указатель у которого сожно свернуть башку» отличается от «указателя у которого башку свернуть нельзя» — науке неведомо.

Или нечаянно — изменить тип.
Рекомедую коллеге глубжее изучить язык о котором он говорит и обнаружить для себя тип ANYPTR.

Динамических массивов в Компонентном Паскале нет, именно по этой причине.
Динамических массивов нет — а вот открытые массивы — вполне себе есть. Для организации совершенно замечательных утечек памяти — их вполне достаточно.

Память в нём — не течёт. Вообще. Никак. Никогда.
Чушь, ложь, враки. Как в любом другом языке с динамическеской памятью утечки вполне типичны.

Ну и? Чего осталось для обсуждения?

И мне не нравится, что вместо понимания сути того, что он говорит, оппоненты лезут в бутылку (а кто-то явно даже карму минусует).
В бутылку тут лезете только вы с вашим «уважаемым» коллегой.

Я вполне понимаю его аргумент в том смысле, что есть языки/рантаймы, в которых, чтобы получить утечки памяти, доступ по некорректным адресам, нарушение структуры кучи и т.п. — надо использовать какие-то явно названные средства
Ok, принято. Рассмотрим языки где утечки памяти требуют специальных конструкций и языки, где это не так.

В первую группу попадут Brainfuck, Malbolge и прочая разная эзотерика. Из мейнстриймовых языков сюда попадут разве что GW-BASIC и FBD. Ни Java, ни C#, ни Oberon, ни Component Pascal — даже рядом не валялись.

И соответственно первые — это Java, .NET, тот же компонентный Паскаль и Оберон, если верить агитаторам за них в этом треде, а второй — C++
Серьёзно? Вы всерьёз хотите сказать, что не можете написать программу, в которой будет утекать память на всех этих языках? Это больше характеризует вас, а не языки, извините.

Утечки памяти — фактически неизбежное следствие достижения язком определённого уровня выразительности. Cобственно любой язык, умеющий оперировать с данными заранее неизвестного объёма их имеет.

Будь то Lisp, Prolog, C# или Component Pascal — неважно. Либо у вас память распределяется во время компиляции программы (и тогда утечек нет по определению), либо — в рантайме (и тогда они рано или поздно появятся).

А вот всё остально, что вы хотите «до кучи» доложить — это да… но это уже совсем другая история.

С тем, что C# безопаснее C++ — никто не спорит. Но, вы уж извините, статья — она как бы про утечки памяти. И вся дискуссия — тоже о них. И вот с ними в Component Pascal и Java — всё также, как и в C++, увы: да, они возможны и нет, это не теоретическая возможность.
> Серьёзно? Вы всерьёз хотите сказать, что не можете написать программу, в которой будет утекать память на всех этих языках? Это больше характеризует вас, а не языки, извините.

Без unsafe и в том же понимании утечки — нет, не могу. И это «характеризует» каждого.
Я тут не считаю ту утечку, которая в managed-среде от забытых ссылок на живые объекты. Для такой ситуации можно, хоть и дороже, найти эти объекты и проанализировать ситуацию точными штатными средствами (откуда эти ссылки, которые их держат, с точностью до объекта и его поля, и т.п.), а главное — корректность кучи не нарушена, данные не перезатёрты, не требуется дорогой учёт предыстории. Для неуправляемой памяти таких средств нет, и начинаются всякие сложные косвенные анализы.

> Рекомедую коллеге глубжее изучить язык о котором он говорит и обнаружить для себя тип ANYPTR.

Про Паскаль я сделал явно оговорку, что тут верю ему на слово — не зная, какая конкретно версия у него использовалась и в каком режиме. Если он не может не использовать тот же anyptr, или как там он зовётся у него, и не может ограничить его использование специальными анклавами — то да, у него нет преимуществ.

> Но, вы уж извините, статья — она как бы про утечки памяти.

А ветка обсуждения — скорее про термины. Я понял, о каких утечках в каком смысле он говорит, а остальные, похоже, даже не попытались.
UFO just landed and posted this here
UFO just landed and posted this here
По поводу тестов — в топ и должна попадать только часть бидов. Тесты, способные сымитировать похожую нагрузку и ответы (и при этом еще и за памятью правильно следить) требуют минимум х2 в инфраструктуру серверов и разработку (и то никаких гарантий обнаружения подобных утечек). Это неоправданно дорого для нас. Потери времени и ресурсов от поисков подобных багов за несколько лет — ничтожны по сравнению с вложениями в тестовую среду уровня, о котором вы говорите.
По поводу отката на старые версии — вся первая часть статьи посвящена этому вопросу, а почему не обнаружили — в конце.
полезно и юнит тесты прогнать под valgrind, с таким багом valgrind показал бы лик на юнит тесте который проверяет обработку большего количества результатов чем max_results
Тесты, способные сымитировать похожую нагрузку и ответы (и при этом еще и за памятью правильно следить) требуют минимум х2 в инфраструктуру серверов и разработку (и то никаких гарантий обнаружения подобных утечек).
Есть ощущение, что и по поводу тестирования приложений (особенно тех, которые затем должны работать в режиме 24/7, да еще и в такой специфической области) у вас такие же заблуждения, как и в отношении стоимости использования unique_ptr.

Посмотрите на современные инструменты. Например, мы для автотестов C++ кода используем набор из Gitlab CI + ASAN + TSAN + gcov + gtest + gbenchmark.
Если проект большой и давно живет, а автотестов в нем нет совсем, то дешевле всего его обмазать внешними, тестами — они позволяют приложив минимум усилий, дать максимум уверенности, что все работает так как надо. (следующий шаг это unit тесты c gtest/gbenchmark, они дадут более информативную диагностику, но их внедрить сильно дороже)


Объем инфраструктуры который требуется:


  • виртуалка(-и) для Gitlab (наверняка у вас уже есть)
  • виртуалка(-и) для запуска GitLab CI runner-ов

Для небольших команд ~10 человек — вся эта инфраструктура спокойно влезет на 2 виртуалки работающие на 1-м физическом сервере.


Общий подход примерно такой:


  1. в билд системе добавляете target-ы для сборки нескольких вариаций инструментированного бинаря:
  2. Делаете набор мок сервисов (например, на том же python, изображающих реальные сервисы, с которыми работает ваше ПО).
  3. Делаете тестовое приложение, которое изображает из себя клиента, и содержит набор тест кейсов (типа в такой то — последовательности сходить в методы API тестируемого ПО/и проверить результаты). В качестве инструмента подойдет тот же python, для него есть ряд фреймворков для этой задачи.
  4. В gitlab-ci добавляете pipeline тестов, в которых рядом запускаются мок сервисы+тестируемое ПО+в том же раннере запускаете по очереди тест кейсы.
  5. По завершению тестов шлете приложению SIGTERM.

Конечно, шаги 2) и 3) потребуют вложить заметное количество усилий, но они себя очень быстро окупят.


Что имеем на выходе:
1) проверка что, ничего не утекло (после выхода бинарь инструментировнный ASAN выдаст список того, что утекло)
2) убедимся, что не было рейсов (TSAN сборка в этом поможет)
3) сможем посмотреть глазами, какой процент кода оказался покрыт нашими тестами (gcov инструментарий даст общий % покрытия и подробный репорт, и покажет в какие строчки попадало управление, а в какие нет). На основании этой информации расширяете покрытие тестами.


И это все работает в весьма скромной аппаратной инфраструктуре.

Может, опубликуете статью, как весь этот зоопарк поднять и настроить?

А вот были таки мысли. Время только найти бы на это

Поражает обилие комментаторов желающих щегольнуть своими познаниями не по делу. Задним умом все умные. Человек пришёл поделится опытом и историей, его закидали какахами. Такое чувство что токсичность и высокомерие особенно присущи сообществу C++.

Автору спасибо за познавательный обзор отладочных инструментов.
Спасибо! По unique_ptr комменты, считаю, по делу, остальное — все больше по оптимизации, интересно подискутировать.
UFO just landed and posted this here
Что вам конкретно не понравилось в разделе tcmalloc? Что не описан LD_PRELOAD? Я писал про линковку (ltcmalloc), а почему оно не работает — внимательно дочитайте этот раздел до конца. И поостынте, уже 10 комментов налепили в такой хреновый пост )
UFO just landed and posted this here
Откуда мне было знать, что оно заведомо не будет работать? Есть полноценный порт, а сноску про недопиленный heap-check в FreeBSD нужно очень постараться, чтоб найти. Цель статьи как раз уберечь других от попыток туда соваться.
По поводу HEAPPROFILE — понятно что не будет работать только с ним, «либо\либо» — это оговорка в статье.
UFO just landed and posted this here
А, ну и еще многих, наверно, зацепило про презрительное отношение к «рестартуем приложение каждые N минут» :)
UFO just landed and posted this here
Вообще-то C++ — это язык, который очень больно бъет по рукам за любой просчет и невнимательность. Особенно когда люди сознательно нарушают давным-давно известные рекомендации. Вот, скажем, про то, что не следует работать с владеющими голыми указателями, говорят с 1990-х годов. Тем не менее, люди во времена C++11 хранят голые владеющие указатели в контейнере и закономерно отгребают проблем. Что здесь остается сказать? Разве что: поделом.

Опять же, вокруг C++ много мифов. Которые, зачастую, рождаются в результате вот таких вот случаев. Одни «профи» отказались от помощи со стороны языка (RAII, unique_ptr) и выбрали для своих задач откровенно неэффективные структуры данных (все-таки std::list для хранения всего лишь указателей — это жуткий перерасход памяти + добавление лишнего уровня косвенности при обращении к элементам, тут уж если нужны были именно свойства list-а, то следовало бы смотреть в сторону интрузивного двухсвязного списка). А потом, когда жопа таки случается, не знают за что хвататься. Другие «профи» прочитают такой отчет о закономерно полученных проблемах и делают для себя вывод о том, что C++ — это отстой, что только Оберон спасет индустрию.

По существу, про C++ все так и есть. По форме — имхо стоит быть терпимее к просчетам других людей.

Когда, скажем, в авиации случается катастрофа, то ее результаты предают огласке и там, почему-то не считают зазорным скрывать факторы, приведшие к катастрофе. В том числе и человеческие. А затем, по результатам, вносят изменения в регламенты, инструкции и уставы для того, чтобы устранить или минимизировать влияние этих факторов.

В разработке ПО же когда случается какая-то беда, почему-то начинаются разговоры про «токсичность и высокомерие» или «стоит быть терпимее к просчетам других людей». Вся обсуждаемая здесь статья — это яркий пример того, как делать не нужно. О чем и следует говорить.

Доля истины в ваших словах есть. Но идеальных людей нет. Нет и идеальных специалистов. С++ — язык который я пронес через 20 лет, это мой первый серьёзный язык, который я учил. Поэтому я мыслил как С++ когда писал программы. Прошли годы и я изучил несколько других концепций в языках программирования. Сейчас, на сегодняшнем уровне своего развития я пришел к пониманию того, что для того чтобы писать код на с++, нужно хорошо выспаться, сделать зарядку, помедитировать, настроится и сконцентрироваться, потом в том состоянии познания дзен быстро кодить, пока вдохновение не ушло. Вовремя остановиться. Рано лечь спать. С++ очень крут, но требует предельной концентрации и всеобъемлющего понимания кода. Сегодня для своих нужд я пришел к языку Ada (не реклама). Я для себя понял, что я точно не робот кодить без ошибок. Компилятор должен облегчать жизнь программистам, ВЕЗДЕ где возможно. Везде идет процесс унификации. Супер спецов всегда будет не хватать, а потребность в софте будет расти. Поэтому инструменты разработки будут развиваться таким путем, чтобы даже средний по уровню специалист писал приемлемый код. Я врач, за последние 20 лет хирургия превратилась из искусства в ремесло (и это нормально). То что раньше мог делать отдельный одаренный Кулибин, сегодня при помощи нового инструмента делает почти любой (Да, Кулибиных в медицине тоже мало). У C++ концептуально с этим трудности. Но ему придется измениться! В первую очередь наверное отказаться от совместимости с С.

Извините, но то, что вы пишете — это оффтоп. Сейчас под любую задачу есть выбор из нескольких языков программирования и инструментов к ним. Никто не заставляет выбирать именно C++. Но если C++ выбран, то имеет смысл придерживаться «уставов, которые были написаны кровью», иначе отстрел конечностей — это лишь вопрос времени. Как у авторов статьи — 7 лет все было хорошо, но закономерный итог их все равно настиг.

Просматривается максимализм. В конкретной моменте с std::list и сырыми указателями, я с вами конечно согласен. Результат закономерен. Как выбирался инструмент программирования, мы не знаем. Может заставляли, может нет. Может автор на тот момент только С++ знал. Использовал то, что знал. Тех задание часто меняется по ходу дела, если оно вообще было. Автор поделился честным опытом, за что ему спасибо. А бывают ситуации, когда тебя ставят перед фактом для тебя мало изученной темы и требуют результат. А так, типа- "Эй… эй… Пацаны. Да вы здесь неочень...", выглядит не солидно. Вот вы тоже пишите статьи и многие благодарны вам за это, и я в их числе, хороший стиль, с удовольствием читаю.

Просматривается максимализм.
Боюсь, что то, что вы принимаете за максимализм, это лишь следствие многолетних наблюдений, когда люди с завидным постоянством наступают на одни и те же грабли.

Это обычный человек увидя последствия ДТП, когда пешеход в нарушение правил ППД выбежал на дорогу и попал под машину, может охать, ахать и причитать «да как же так, да кто же мог подумать». А немолодой сотрудник ГАИ или врач скорой с многолетним опытом отнесется к произошедшему, думаю, совсем иначе. Собственно, у меня такая же профдеформация.

Автор поделился честным опытом, за что ему спасибо
Да, автор поделился. За это ему спасибо. Но написанное в итоге следует воспринимать как горький урок. И ценность статьи, как мне кажется, вовсе не в перечне инструментов, с которыми автору довелось познакомиться. А именно в том, что здесь идет наглядная демонстрация: нарушил правила — отгреб по полной программе.

Поэтому я не понимаю комментариев вот такого вида. Из-за чего эта подветка обсуждения и появилась.

Ладно. Мы поняли друг друга. Но тем не менее добрее надо быть. Все в одной лодке, а нам еще другие миры покорять))) Работы непочатый край)))

Просматривается максимализм.

Кто виноват в том, что абстрактная программа работает некорректно — её автор или ЯП, на котором она написана?

Про tcmalloc:
В комплекте с pprof это очень мощный инструмент, который позволяет прямо в RUNTIME в любой момент следить за всей памятью выделенной приложением, даже tcmalloc на вашем хосте не умеет leak detection.


Выглядит как то так:


pprof --inuse_objects http://127.0.0.1:9088/debug/pprof/heap
Using remote profile at http://127.0.0.1:9088/debug/pprof/heap.
Fetching /pprof/heap profile from http://127.0.0.1:9088/debug/pprof/heap to
  /Users/ogerasimov/pprof/reindexer_server.1524898743.127.0.0.1.pprof.heap
Wrote profile to /Users/ogerasimov/pprof/reindexer_server.1524898743.127.0.0.1.pprof.heap
Welcome to pprof!  For help, type 'help'.
(pprof) top
Total: 976926 objects
  269535  27.6%  27.6%   446325  45.7% reindexer::IndexUnordered::Upsert
  263687  27.0%  54.6%   263687  27.0% reindexer::make_key_string
  139063  14.2%  68.8%   139063  14.2% __hash_table::__construct_node_hash
  138999  14.2%  83.0%   138999  14.2% reindexer::PayloadValue::PayloadValue
  110115  11.3%  94.3%   330296  33.8% reindexer::IndexStore::Upsert
   35548   3.6%  98.0%    37827   3.9% btree::btree::rebalance_or_split
   11212   1.1%  99.1%    11212   1.1% reindexer::h_vector::reserve
    1041   0.1%  99.2%    39690   4.1% btree::btree::internal_insert
    1008   0.1%  99.3%    28663   2.9% reindexer::IdSet::Add
     960   0.1%  99.4%      960   0.1% leveldb::::HandleTable::Resize

Что бы это работало, в свое приложение надо добавить весьма тривиальную обработку вызова нескольких http методов для pprof

Спасибо! Какой оверхед за использование всех этих доп. обвесов?

По памяти — минимальный, практически не виден на приборах. По скорости работы зависит от хоста, примерно так:
На Linux включение профайлинга кучи в tcmalloc снижает скорость работы приложения в среднем раз в 5-10.
На OSX (и возможно на BSD) — снижение скорости в 1.5 — 2 раза.

Sign up to leave a comment.

Articles