К вопросу об ФЯ
Слушай, а расскажи, как статическая типизация нам жить и строить помогает. Действительно интересно послушать аццов программирования.
Почему сложнее? Упал из-за нехватки памяти, странно, что Хаскель не обрабатывает эту ситуацию правильно, в нем такое часто может случаться.
Потому что в C все подобные вещи легко контролируются. Я могу запустить программу под отладчиком, словить segfault, посмотреть стек вызовов, посмотреть значения переменных и памяти, что где запорчено. Про каждый блок памяти можно сказать, кто и когда его выделил (и почему не освободил). Можно навставлять отладочных printf-ов. На худой конец, можно запустить valgrind. В любом случае, существуют отработанные годами методы отладки memory corruption и они работают (все подобные проблемы, с которыми я сталкивался, рутинными методами находились и устранялись в течении нескольких часов).
В случае с Haskell ситуация другая. Программист не контролирует программу. Программа на Haskell ведет себя как черный ящик. Что и в каком порядке будет выполняться не известно. Так же неизвестно, кто и когда будет выделять память, а также когда она будет освобождаться. Непонятно, как вставить отладочный printf на выделение памяти (printf имеет побочный эффект, а как следствие придется менять тип функций, что приведет к изменению порядка вычислений, а значит баг может не воспроизвестись). Можешь объяснить, как производить отладку утечек памяти в условиях такой неопределнности? Я, например, не понимаю. Вижу только один вариант --- скачать исходники GHC и втыкать в них до полного просветления. И то не факт, что поможет.
Я на самом деле плохо в этом разбираюсь. Речь идет не о статической (static а о строгой (strong) типизации. Суть в том, строгая типизация гарантирует отсутствие некоторых (не всех) видов ошибок во время работы. Сторонники строгой типизации утверждают, что эти виды ошибок наиболее часто встречаются и сложно отлаживаются, поэтому их отсутствие является большим плюсом.
Наиболее понятное (из тех, что я видел) введение в теорию типов содержится тут
Еще рекомендую эту книгу. Есть у меня в электронном виде (chm). Если в сетке нет --- могу закачать.
> Действительно интересно послушать аццов программирования.
+1. Ждем появления главных атцов в этом треде.
В случае с Haskell ситуация другая. Программист не контролирует программу. Программа на Haskell ведет себя как черный ящик. Что и в каком порядке будет выполняться не известно. Так же неизвестно, кто и когда будет выделять память, а также когда она будет освобождаться. Непонятно, как вставить отладочный printf на выделение памяти (printf имеет побочный эффект, а как следствие придется менять тип функций, что приведет к изменению порядка вычислений, а значит баг может не воспроизвестись). Можешь объяснить, как производить отладку утечек памяти в условиях такой неопределнности? Я, например, не понимаю. Вижу только один вариант --- скачать исходники GHC и втыкать в них до полного просветления. И то не факт, что поможет.Тогда тебе надо изобрести машину времени и вернуться лет на 30 назад. Не один Haskell такой, а и .NET, Java, многочисленные языки для Web. Haskell, конечно, идет слегка дальше, чем они, но несущественно.
Утечек памяти в Haskell'e быть не должно, если есть, то это бага компилятора и ей должны заниматься разработчики. В Haskell'e может течь память из-за накапливания недовычисленных выражений, но это не та утечка памяти, что в С, где концов не найдешь. Эта проблема известная и значит есть механизмы ее выявления, надо только узнать у тех, кто в курсе дела.
Не один Haskell такой, а и .NET, Java, многочисленные языки для WebОбоснуй.
Что тут обосновывать-то? Все они используют байт-код и сборку мусора.
Тогда тебе надо изобрести машину времени и вернуться лет на 30 назад. Не один Haskell такой, а и .NET, Java, многочисленные языки для Web. Haskell, конечно, идет слегка дальше, чем они, но несущественно.Не согласен. Garbage collection --- традиционная вещь, хорошо изученная и отлаженная. Сама по себе сборка мусора ни чего не портит. Грубо говоря, если программа с ручным malloc/free может работать в X Мбайт памяти, то убрав из нее malloc/free и поставив правильный GC, она также сможет работать, занимая не более X Mбайт памяти. То есть, GC принципиально не может сломать работающую программу. (Конечно, он может сильно тормозить, но не будем вдаваться в подробности). В .NET, Java, Lisp --- именно так.
Здесь ситуация совсем другая. Увеличение объема используемой памяти (я бы даже не назвал это утечкой) возникает из-за ленивых вычислений. При этом, что в каком порядке будет вычисляться нигде явно не определяется. Не известно, какие вычисления будут откладываться как ленивые, а какие вычисляться. Этот выбор делает компилятор. Фишка в том, что программа, которая при обычном (eager) выполнении требует X Mб памяти, при ленивом выполнении может потребовать Y Mb памяти, где Y >> X. При этом может оказаться, что Y >> S >> X, где S --- объем доступной памяти.
То есть, компилятор сломает работающую программу.
Утечек памяти в Haskell'e быть не должно, если есть, то это бага компилятора и ей должны заниматься разработчики. В Haskell'e может течь память из-за накапливания недовычисленных выражений, но это не та утечка памяти, что в С, где концов не найдешь.Это не простая утечка памяти. Все возникает из-за ленивости, которая работает не пойми каким образом.
Эта проблема известная и значит есть механизмы ее выявления, надо только узнать у тех, кто в курсе дела.Кому известная? Люди из Haskell-cafe mailing list про такие механизмы не знают. Предлагают разные танцы с бубном по типу "попробуй туда поставить x == x". Если тебе известно как ее решать, то напиши.
Решение о том, запускать очередную функцию прямо сейчас или сделать thunk, должно приниматься не на этапе компиляции, а в run-time с учетом объема доступной памяти. Насколько я понимаю, в GHC не так (кто-нибудь может подтвердить или опровергнуть это?). Причем, как правильно этот размер памяти учитывать, никто не знает. То есть, это принципиальная проблема, а не просто мелкий баг в GHC.
Не согласен. Garbage collection --- традиционная вещь, хорошо изученная и отлаженная. Сама по себе сборка мусора ни чего не портит.+1 Мы понимаем друг друга. А вот ерунду пишет.
Functional languages do not give any new possibilities compared to procedure languages. So what's the purpose of using them? Just convenient notation? Well... Whom how, KAK GOVORIT'SA...
Ты не в теме.
http://en.wikipedia.org/wiki/Lazy_evaluation
http://en.wikipedia.org/wiki/Functional_programming
Так почему ленивые вычисления — это "к вопросу о ФЯ"?
Самый ФЯ язык — LISP, вроде не ленивый.
Python — может быть ленивым, но он не ФЯ.
http://en.wikipedia.org/wiki/Common_Lisp
http://moonbase.rydia.net/software/lazy.rb/ — ленивые вычисления для Ruby
В Scheme тоже нет встроенных ленивых вычислений
http://en.wikipedia.org/wiki/Functional_programming
Так почему ленивые вычисления — это "к вопросу о ФЯ"?
Самый ФЯ язык — LISP, вроде не ленивый.
Python — может быть ленивым, но он не ФЯ.
http://en.wikipedia.org/wiki/Common_Lisp
Common Lisp is a multi-paradigm programming language that:
* Supports programming techniques such as imperative, functional and object-oriented programming.
* Is dynamically typed, but with optional type declarations that can improve efficiency.
* Is extensible through standard features such as Lisp macros (compile-time code rearrangement accomplished by the program itself) and reader macros (extension of syntax to give special meaning to characters reserved for users for this purpose).
http://moonbase.rydia.net/software/lazy.rb/ — ленивые вычисления для Ruby
lazy evaluation...
For lazy evaluation, the facilities are similar to those provided by R5 Scheme. There are two functions: promise (similar to Scheme's delay) which takes a block for later evaluation, and demand (similar to Scheme's force which forces its evaluation (if necessary) and returns its cached result.
Unlike some Scheme implementations, it is safe to pass ordinary values to demand. Promises are also transparent, meaning that in most cases an evaluated promise is not distinguishable from the actual result object it wraps.
To avoid locking overhead for single-threaded applications, promises will not be threadsafe unless you require 'lazy/threadsafe'
В Scheme тоже нет встроенных ленивых вычислений
http://en.wikipedia.org/wiki/Lisp_programming_language
As one of the earliest programming languages, Lisp pioneered many ideas in computer science, including tree data structures, automatic storage management, dynamic typing, object-oriented programming, and the self-hosting compiler
Автор не в теме?
(а именно, вычисление происходит непосредственно перед использованием)
Согласен, название не совсем точное. Имелось ввиду то, что традиционная точка зрения о том, что ФЯ автоматизирует множество задач не совсем верна, так как при этой автоматизации тут еще возникает масса подводных камней.
> Самый ФЯ язык — LISP, вроде не ленивый.
Кто тебе такое сказал?
Lisp совсем не ФЯ. Ботай Functional programming is not self-modifying code и Functional programs as executable specifications
(обсуждение в конце на стр. 387, ответ проф. Wadler).
Цитирую (сорри за картинку, там PDF из сканов):
> Python — может быть ленивым, но он не ФЯ.
Где это Python ленивый?
> http://moonbase.rydia.net/software/lazy.rb/ — ленивые вычисления для Ruby
В любом языке, даже С++, можно сделать ленивые вычисления, с помощью дополнительных библиотек.
Речь идет о языках, где эта ленивость встроена в язык и где все вычисляется лениво (а не только то, что скажет пользователь). Из таких языков --- только ФЯ.
Покажи не ФЯ, у которого есть high-order types, type classes.
> Автор не в теме?
Я прекрасно знаю что в Lisp строгая динамическая типизация. Чем это противоречит моему посту? Речь о другом. О том, что попытка ФЯ скрывать многие вещи (в частности порядок вычислений) приводит к сложным проблемам.
> (а именно, вычисление происходит непосредственно перед использованием)
Хм. Интересно. То есть, любое вычисление откладывается пока явно не потребуется? Мне казалось компилятор делает strictness analysis. А то ведь тормоза будут жуткие... Ты уверен, что объем свободной памяти не учитывается при запуске очередной функции лениво?
Еще: если я написал a + b, то нужны и a, и b. В каком порядке будут вычиляться? Слева направо?
Ну просто вы обсуждаете то, о чем не имеете представления.
> > Самый ФЯ язык — LISP, вроде не ленивый.
> Кто тебе такое сказал?
>Lisp совсем не ФЯ. Ботай Functional programming is not self-modifying code и Functional programs as executable specifications
>(обсуждение в конце на стр. 387, ответ проф. Wadler).
Обалдеть. Ты не умеешь пользоваться гуглом?
Какой-то там скан обсуждения в конце книги
http://en.wikipedia.org/wiki/List_of_functional_programming_...
http://en.wikipedia.org/wiki/Functional_programming
и даже по-русски
>> Python — может быть ленивым, но он не ФЯ.
> Где это Python ленивый?
itertools и lambda. сам же ответил:
> В любом языке, даже С++, можно сделать ленивые вычисления, с помощью дополнительных библиотек.
> Речь идет о языках, где эта ленивость встроена в язык и где все вычисляется лениво (а не только то, что скажет пользователь). Из таких языков --- только ФЯ.
Так ты о теплом или о мягком? Пока что все в одной куче. Видимо, у тебя ФЯ=Haskell
Не знаком с классиками? Это очень известная статья, и должно быть стыдно так про нее писать.
> http://en.wikipedia.org/wiki/List_of_functional_programming_...
> http://en.wikipedia.org/wiki/Functional_programming
> и даже по-русски
Да мало ли что там написано во всяких википедиях! Сдается, мне профессор David Turner побольше понимал в функциональном программировании, чем ты или авторы статьи в википедии.
На Lisp можно писать в функциональном стиле, там для этого все есть. Хотя, конечно, императивных фич в нем тоже не мало, да и требования оптимизации хвостовой рекурсии нет. Вот Scheme уже фактически 100% функциональный язык.
Еще: если я написал a + b, то нужны и a, и b. В каком порядке будут вычиляться? Слева направо?некорректный вопрос
в хаскеле вычисляться будет по мере надобности
также не забывай, что a + b это сахар для (+ a) b
Наглая ложь.
delay и force описаны даже в R4RS.
Просто Схема, как и многие другие ФЯ, жадный язык,
то есть по умолчанию вычисления жадные.
---
...Я работаю антинаучным аферистом...
Ада.
> попытка ФЯ скрывать многие вещи (в частности порядок вычислений) приводит к сложным проблемам.
У тебя к ошибке _времени_исполнения_ приводит не функциональность, а ленивость,
которую, при некотором желании, можно сделать и в императивном языке ("call by value" в Алголе-60).
Изменение порядка вычислений может приводить лишь к тому,
что ошибочное выражение (A B C ...) будет _вычислено_,
а не свалится по нехватке памяти или ещё чего-нибудь,
если при вычислении A, B, C или чего остального
будет принято какое-нибудь другое продолжение.
---
...Я работаю антинаучным аферистом...
> Ада.
Ответ неверный. Иди ботай что такое type classes.
> У тебя к ошибке _времени_исполнения_ приводит не функциональность, а ленивость,
которую, при некотором желании, можно сделать и в императивном языке ("call by value" в Алголе-60).
Ладно. Более конструктивно. Вот я написал программу, она сжирает кучу, потом падает с segmentation fault. Что мне делать? Ссылки на статьи приветствуются.
> Изменение порядка вычислений может приводить лишь к тому,
> что ошибочное выражение (A B C ...) будет _вычислено_,
Ты неправильно понял. Выражение не ошибочное, а корректное. Проблема в том, что корректное выражение не вычисляется по причине неправильного использования памяти.
>> Ада.
> Ответ неверный. Иди ботай что такое type classes.
"Type classes correspond to parameterized abstract classes."
В чём противоречие специфицированному "generic package"?
>> У тебя к ошибке _времени_исполнения_ приводит не функциональность, а ленивость,
>> которую, при некотором желании, можно сделать и в императивном языке ("call by value" в Алголе-60).
> Ладно. Более конструктивно.
> Вот я написал программу, она сжирает кучу, потом падает с segmentation fault.
> Что мне делать?
Не использовать ленивость?
Это свойство действительно приводит к не очень очевидным последствиям,
особенно если оно используется по умолчанию.
> Ссылки на статьи приветствуются.
Встречный вопрос: а есть способ преодолеть ленивость вычислений в хаскеле?
>> Изменение порядка вычислений может приводить лишь к тому,
>> что ошибочное выражение (A B C ...) будет _вычислено_,
> Ты неправильно понял. Выражение не ошибочное, а корректное.
> Проблема в том, что корректное выражение не вычисляется
> по причине неправильного использования памяти.
Последнее относится к работе интерпретатора, не так ли?
А первое, из-за жадности --- свойство языка.
Отсюда вывод: источник ошибки надо искать в устройстве интерпретатора.
Для начала можно проверить, как относятся к твоей программе
разные интерпретаторы: глазговский, нотингемский.
Ну, и читать статьи про механизмы реализации того, что там наворочено.
---
...Я работаю антинаучным аферистом...
КАК? Язык сам ленивый, в нем все ленивое. Это свойство языка. Есть некоторые хаки, например x == x. Но тут две проблемы.
1) Это именно хак. Было бы логично, если оптимизатор заменял x == x на True. Непонятно как это поведет себя в общем случае, на других компиляторах. Если в составе x есть функция, как их сравнивать?
2) Необходима какой-то метод, принцип, указывающией, где расставлять подобные конструкции. Я пока такого принципа не вижу. То что обсуждают в mailing list указывает на то, что в большинстве случаев это делается хаотически.
> Встречный вопрос: а есть способ преодолеть ленивость вычислений в хаскеле?
Есть. Например, x == x, seq, но см. выше.
> Отсюда вывод: источник ошибки надо искать в устройстве интерпретатора.
Во-первых, не интерпретатор, а компилятор. Во-вторых, все кроме GHC --- отстой, не поддерживают жизненно важных фич.
С одной стороны --- да, ошибка в интерпретаторе. Но с другой стороны, если язык принципиально не дает программисту возможностей указывать, в каком порядке вычислять, то неизбежно возникнет подобная ситуация. То есть это становится свойством языка, а не конкретной реализации.
> КАК?
Писать на жадном языке.
> Язык сам ленивый, в нем все ленивое. Это свойство языка.
"К вопросу о ФЯ," это свойство ленивого ФЯ или, если быть ещё точнее, хаскела.
Другие языки могут каким-то образом совмещать ленивое и жадное выполнение.
> Есть некоторые хаки, например x == x. Но тут две проблемы.
> 1) Это именно хак. Было бы логично, если оптимизатор заменял x == x на True.
> Непонятно как это поведет себя в общем случае, на других компиляторах.
Можешь проверить.
Я не думаю, что в мире настолько много компиляторов хаскела.
От итогов проверки зависит, какой из компиляторов надо улучшать.
> Если в составе x есть функция, как их сравнивать?
Структурно?
Можно придумать какие-то способы, вопрос только зачем,
если это является по сути хаком.
> 2) Необходима какой-то метод, принцип, указывающией,
> где расставлять подобные конструкции.
> Я пока такого принципа не вижу.
Почему? Мы уже ведь установили, что по сути расстановка этих хаков
производится в целях оптимизации использования памяти,
то есть к этому применимы общие методы оптимизации,
наподобие "profile then optimize".
> То что обсуждают в mailing list указывает на то,
> что в большинстве случаев это делается хаотически.
Если знать устройство компилятора, то можно попробовать вывести какие-то эвристики.
Последние можно выводить и чисто опытным путём,
но для этого надо достаточно много работать.
>> Отсюда вывод: источник ошибки надо искать в устройстве интерпретатора.
> Во-вторых, все кроме GHC --- отстой, не поддерживают жизненно важных фич.
Надо улучшать компилятор.
Кстати, а "жизненно важные фичи" относятся к какому роду?
FFI и готовые привязки? Библиотеки?
Расширения языка внешнеязыковыми средствами?
Можешь расказать подробности?
> С одной стороны --- да, ошибка в интерпретаторе.
> Но с другой стороны, если язык принципиально не дает программисту возможностей указывать,
> в каком порядке вычислять, то неизбежно возникнет подобная ситуация.
> То есть это становится свойством языка, а не конкретной реализации.
Про неизбежность ты зря.
Если бы у тебя было достаточно памяти, ты не видел бы падений по нехватке.
Разве что медленную работу программы.
---
...Я работаю антинаучным аферистом...
> Встречный вопрос: а есть способ преодолеть ленивость вычислений в хаскеле?эээ
Есть. Например, x == x, seq, но см. выше.
а зачем же тогда в хаскеле восклицательный знак?
он вроде прибивает ленивость
Вот я написал программу, она сжирает кучу, потом падает с segmentation fault. Что мне делать? Ссылки на статьи приветствуются.в ghc же есть профайлер
правда не уверен что он там память показывает
я его использовал чтобы смотреть какие места в проге медленные
> наподобие "profile then optimize".
Нет нормальных профайлеров для Haskell. Я пробовал пользоваться --- всякую лажу выдают, ничего понять невозможно.
> Кстати, а "жизненно важные фичи" относятся к какому роду?
> FFI и готовые привязки? Библиотеки?
> Расширения языка внешнеязыковыми средствами?
> Можешь расказать подробности?
Да, без FFI и стандартных библиотек не жизнь.
Но даже если их небрать, то все равно есть проблемы. Некоторые компиляторы не поддерживают даже такие простые чисто функциональные вещи, как Control.Monad.Writer (посмотри тут, только те, которые помечены base входят в стандарт). NHC генерирует дико неоптимальный код (тормозит в десятки раз по сравнению с тем же GHC).
Это не профайлер, а говно.
Профайлеры бывает intrusive (который требует изменения программы) и non-intrusive (который не требует). intrusive profiling --- заведомо порочная идея, так как при этом профилируется не та программа, которую ты написал, а какая-то другая. gprof, профайлер из ghc --- говно. Единственный профайлер, который я видел под Linux --- это oprofile. Он использует аппаратные регистры процессора, что позволяет ему видеть какую часть времени съедают какие инструкции. Плюс к этому можно смотреть где переходы плохо предсказаны, где кэш-промахи и другие важные характеристики. Еще говорят, VTune бывает для Linux, но у меня не получилось его заставить нормально работать (возможно из-за кривизны рук).
> правда не уверен что он там память показывает
> я его использовал чтобы смотреть какие места в проге медленные
Расскажи как. Я вот свою пытаюсь профилировать --- выдает какую-то фигню. Типа сотня функций по 1-0.1% и main который 99%. Что с этим делать?
Когда делаешь такие сильные заявления, чуточку притормаживай.
> так как при этом профилируется не та программа, которую ты написал, а какая-то другая.
Это если есть оптимизация через границу конструкций.
> Он использует аппаратные регистры процессора,
> что позволяет ему видеть какую часть времени съедают какие инструкции.
> Плюс к этому можно смотреть где переходы плохо предсказаны,
> где кэш-промахи и другие важные характеристики.
Это всё слишком низкоуровневые вещи, на которые в твоём случае можно начхать.
Они ничего особенно не показывают, разве что работу компилятора.
> Типа сотня функций по 1-0.1% и main который 99%. Что с этим делать?
Похоже, что ленивость и здесь всё ломает.
Вроде того, все вложенные вычисления отложены до main,
которое жадное по определению.
Попробуй пожадничать на нужных подвыражениях.
---
...Я работаю антинаучным аферистом...
Приведи примеры, когда это не верно.
> Это если есть оптимизация через границу конструкций.
Во всех нормальных компиляторах такая оптимизация должна быть.
Даже без оптимизации проблема все равно проявляется. Допустим у тебя есть функции которые работают 0.1, 0.01, 0.001 соответственно. Gprof вставляет код при каждом входе/выходе из функции, он достаточно тяжелый, например, его сложность 1. Тогда профайлер показывает, что время относится как 1.1 : 1.01 : 1.001, то есть три функции жрут примерно равное количество времени. В то же время реальное соотношение будет 100 : 10 : 1.
Практика показывает, что intrusive profiling не дружит с inlining. А последнее очень сильно влияет на производительность C++ программ. Например, стоит мне не встроить std::vector<>::operator[], как скорость программы упадет в несколько раз и вообще вся картина распределения времени изменится.
> Это всё слишком низкоуровневые вещи, на которые в твоём случае можно начхать.
> Они ничего особенно не показывают, разве что работу компилятора.
В Haskell --- может быть. Но в C++ это очень актуально. Я, например, могу видеть тело функции и смотреть, какая инструкция сожрала сколько процентов. Это важно при оптимизации. Например, один раз я заметил, что основную часть времени в одной функции жрет idiv. Переписал a / b виде a * c, где c = 1/b, скорость увелиличилась на 11%. Были и другие случаи.
> Похоже, что ленивость и здесь всё ломает.
> Вроде того, все вложенные вычисления отложены до main,
> которое жадное по определению.
Есть подозрение, что на всех больших программах, которые никто специально не оптимизировал, картина примерно такая же будет. То есть это типичный случай. А если профайлер не работает в типичном случае --- это нехорошо.
>> Когда делаешь такие сильные заявления, чуточку притормаживай.
> Приведи примеры, когда это не верно.
Когда ты профилируешь использование памяти в жадной программе,
при условии, что нет оптимизации через границу конструкций (или ей можно пренебречь).
>> так как при этом профилируется не та программа, которую ты написал, а какая-то другая.
> Во всех нормальных компиляторах такая оптимизация должна быть.
В большинстве случаев (или, по крайней мере, во многих) ей можно пренебречь.
> Даже без оптимизации проблема все равно проявляется.
> Допустим у тебя есть функции которые работают 0.1, 0.01, 0.001 соответственно.
> Gprof вставляет код при каждом входе/выходе из функции,
При _каждом_?!
Я не ослышался?
Я тебя правильно понимаю, что для "f(g(a, b h(c, d" код вставится для всех трёх функций?
> он достаточно тяжелый
Это неизлечимый случай.
> Практика показывает, что intrusive profiling не дружит с inlining.
Пока что не понял, где засада.
>> Это всё слишком низкоуровневые вещи, на которые в твоём случае можно начхать.
>> Они ничего особенно не показывают, разве что работу компилятора.
> В Haskell --- может быть. Но в C++ это очень актуально.
Давай пока будем обсуждать высокоуровневые языки программирования.
Твой пример с делением присущ именно низкоуровневой оптимизации.
>> Похоже, что ленивость и здесь всё ломает.
>> Вроде того, все вложенные вычисления отложены до main,
>> которое жадное по определению.
> Есть подозрение, что на всех больших программах,
> которые никто специально не оптимизировал,
> картина примерно такая же будет.
> То есть это типичный случай.
> А если профайлер не работает в типичном случае --- это нехорошо.
Есть подозрение, что ленивые языки не есть типичный случай.
---
...Я работаю антинаучным аферистом...
...
>> Можешь расказать подробности?
> Да, без FFI и стандартных библиотек не жизнь.
> Но даже если их небрать, то все равно есть проблемы.
...
> посмотри тут
Лучше бы ты привёл ссылку на сравнительный обзор.
Наверняка же такой должен быть.
---
...Я работаю антинаучным аферистом...
Тогда профайлер показывает, что время относится как 1.1 : 1.01 : 1.001У меня вопрос: а бывают профайлеры, которые не учитывают свое собственное время?
> Я не ослышался?
> Я тебя правильно понимаю, что для "f(g(a, b h(c, d" код вставится для всех трёх функций?
Да. Запусти gcc -pg -S и почитай листинг. В начале каждой функции стоит call mcount.
> Это неизлечимый случай.
Это повседневный случай.
>> Практика показывает, что intrusive profiling не дружит с inlining.
>Пока что не понял, где засада.
Привожу пример типичной "работы" gprof.
Берем программу:
#include <stdio.h>
int g(int n)
{
return n * n + n - 5;
}
int f(int x)
{
return g(x) + x;
}
int main
{
int s = 0;
for (int k = 0; k < 100; k++) {
for (int p = 0; p < 100000000; p++) {
s += f(p);
}
}
printf("%d\n", s);
return 0;
}
Компилируем g++ -O3 -pg 1.c. Запускаем gprof
$ gprof a.out
gprof: gmon.out file is missing call-graph data
Правильно, компилятор все синлайнил, никакие call mcount не вызывались, поэтому gprof не работает.
Ладно, дальше:
$ gprof -Q a.out
Flat profile:
Each sample counts as 0.01 seconds.
% cumulative self self total
time seconds seconds calls Ts/call Ts/call name
100.00 25.39 25.39 main
% the percentage of the total running time of the
time program used by this function.
Потрясающе! Очень информативная информация. Как мне узнать сколько процентов времени выполняется f? Сколько g?
Бывают профайлеры (например oprofile или vtune overhead от которых одинаков для всех участков кода (например 1-2%). Поэтому соотношение времен работы отдельных частей программы они показывают правильно. Именно это обычно и нужно.
> Запусти gcc -pg -S и почитай листинг. В начале каждой функции стоит call mcount.
Офигеть.
>> Это неизлечимый случай.
> Это повседневный случай.
Оказывается, синдром NIH бывает полезен.
>>> Практика показывает, что intrusive profiling не дружит с inlining.
>> Пока что не понял, где засада.
> Привожу пример типичной "работы" gprof.
...
> gprof: gmon.out file is missing call-graph data
> Правильно, компилятор все синлайнил, никакие call mcount не вызывались, поэтому gprof не работает.
gprof не работает ещё раньше, когда не вставляет вызовы.
Либо поддержка со стороны gcc очень ограничена.
---
...Я работаю антинаучным аферистом...
Оставить комментарий
Landstreicher
Сторонники ФЯ обычно утверждают что он избавляет программиста от множества проблем. Например, не нужно заниматься выделением и освобождением памяти. Строгий контроль типов не даст программе сделать "что-нибудь плохое" ("Well-typed programs never go wrong", R. Milner). Некоторые форумчане например, говорили мне, что если программа на Haskell или ML прошла проверку типов, то она никогда не упадет в Segmentation fault.Однако, в реальности ФЯ подносит на множество сюрпризов. Вот хороший пример. Программист написал правильную программу, она прошла проверку типов, но затем упала в segfault. Мне кажется борьба с подобного рода вещами гораздо сложнее, чем тот же отлов memory corruption в программе на C, для которого есть развитые средства, например Valgrind.
Таким образом, тезис о том, что разработка приложений на ФЯ упрощается (по сравнению с ИЯ) весьма спорный.