Пример: берем GSM FR, 13kbps. 13kbps = 97500 bytes per minute. 97500 / (140 * 2 байта Unicode) = 348 сообщений. Одна минута прослушивания voice mail условно стоит 10 центов. Один SMS - стоит 5 центов. Таким образом, мы переплачиваем в 174 раза. :)
Голосовую почту они хранят? Входит в базовый тариф практически в любой стране мира и у любого оператора? Посчитайте, сколько SMS влезут в ОДНУ МИНУТУ записи разговора при любом GSM кодеке.
Да ну ладно, :) все равно эта услуга стоит самой сети в сотни раз меньше, чем клиенту. Просто спрос определяет предложение, а операторам и владельцам инфраструктуры "очень деньги нужны". :)
Все равно расходы ничтожные и норма прибыли на SMS у операторов измеряется тысячами процентов. Да и на передаче голоса они тоже делают мега-бабло из воздуха. При этом будучи реалистом и сторонником капитализма :) я понимаю, что спрос определяет предложение.
Но уже скоро их сверх-доходам придет конец, как это уже случилось со сверх-доходами у наземных телефонных операторов (в развитых странах).
Вообще-то есть ОДНА техника для языков со сборкой мусора, которая не вносит больших накладных расходов. Она известна давно и используется во многих динамических средах. Могу рассказать более и менее подробно, так как сам написал такой аллокатор для одного из бывших проектов.
Делается это так - когда кончается вся память в куче, аллокатор останавливает все треды программы средствами ОС (suspend thread). При этом ОС сохраняет контексты всех тредов (дампы регистров) в памяти. Так как типовая ОС (ни одна известная мне) не имеет вызова для массовой остановки тредов по списку, то останавливаем мы их в цикле. Вызов приостановки треда блокирующий и ждущий, так как иначе мы не были бы уверены, что thread остановлен и его регистры сохранены. В результате по мере остановки других тредов мы сами успеваем (как правило) потерять пару раз управление из-за переключения контекстов. Генерируется куча IPI на мультипроцессорной машине...
Наконец, все треды остановлены. Наш аллокатор запрашивает у ОС контексты всех тредов - так как ему нужно просканировать ВСЕ значения во ВСЕХ регистрах прикладной программы. Мы ищем в каждом из регистров значение, внешне ПОХОЖЕЕ на указатель на блок памяти, находящийся под контролем нашего аллокатора.
Когда находим такой "указатель" - маркируем соответствующий блок как зафиксированный, как и ВСЕ блоки, на которые он ссылается (для этого мы должны получить от компилятора или еще откуда-то описание всех структур данных в программе и рекурсивно обойти все блоки по ссылкам из данного блока).
Далее лезем во все стековые фреймы всех замороженных тредов, находим там все указатели на память и маркируем все блоки, на которые они ссылаются, как живые. И блоки на которые ссылаются эти блоки и т.д., рекурсивно. То есть, применяем классический mark-and-sweep алгоритм для разметки живых блоков и дальнейшего уничтожения мусора.
Блоки, которые маркированы как живые, но не помечены как зафиксированные - можно переместить, если мы в состоянии откорректировать ссылки на них. Это нужно если мы используем generational аллокатор. Без такого аллокатора вообще вся память при mark-and-sweep стратегии сборки мусора будет изведена в момент.
Перемещение блоков - отдельная песня, так как мы должны иметь возможность откорректировать все ссылки на перемещаемые блоки – значит нам нужно уметь трассировать их в обратную сторону.
Вот так это работает.
Требуется поддержка на уровне компилятора - но это не страшно, если у нас свой язык, а не C[++]. Очень большие накладные расходы и торможение в момент приостановки тредов - параллельность вся идет нафик. НО! Так как при использовании ХОРОШЕГО generational аллокатора сборка мусора происходит ОТНОСИТЕЛЬНО РЕДКО, то с этим можно мириться. Накладные расходы на поддержание структур тоже в целом приемлемы, на фоне выгод для программиста от языка без указателей.
Однако есть ВАЖНОЕ ограничение. Этот прием нормально работает (оправдывает себя) только с mark-and-sweep стратегией выявления мусора и подразумевает stopping the world - остановку всех тредов на время сборки.
Из-за этого данная стратегия, наиболее эффективная для исполнения кода на предельной скорости, не подходит для real-time приложений и для серверов, где нужно быстрое время отклика.
Так как в НЕПРЕДСКАЗУЕМЫЕ ЗАРАНЕЕ моменты система "зависает" на неопределенное по большому счету время и начинает собирать мусор. При этом НИ ОДНИН СОДЕРЖАТЕЛЬНЫЙ тред программы не может работать - все стоит раком. Сервер не отвечает на запросы, пока это не кончится, растут очереди. Далее все оживает и начинает снова работать быстро - до следующей сборки мусора...
Кэши все равно трешатся намного сильнее, чем в программах на C[++].
Но, тем не менее, несмотря на все эти минусы, ПРАКТИЧЕСКИ это лучший IMHO алгоритм. Несмотря на его идейную кривизну - например, на то, что иногда он случайное число в регистре какого-то треда ложно трактует как указатель на блок памяти. Все-таки это всего-лишь неэффективность, но не bug.
В обычной системе (в Linux, Windows) мы не имеем контроля над моментом переключения контекстов. Происходит прерывание от таймера - и они переключают контекст, не интересуясь тем, что в этот момент делает машинный код юзерской программы. Поэтому если напрямую работать с памятью по указателю или убрать всякие флаги/барьеры, запись в которые ест время (и создает трафик на межпроцессорной/между-ядерной шине), то все вообще накроется медным тазом после переключения контекстов. Можно статическим анализом убрать сами аллокации/деаллокации, часть из них переведя в заказы на стеке - согласен. Но предположим, что у нас настоящая программа, работающая со структурами данных, а не просто вычислительные циклы вложенные. И динамический заказ памяти в ней делается содержательно и на стек его не перевести никак. В результате имеем кучу проблем - начиная от поглощения всей памяти за короткое время (с чем борются generational аллокаторами) и заканчивая самым сложным - вообще плохой увязкой таких языков с многопроцессорностью/многоядерностью. А железо покупать особенное - это для тех, кто уже имел неосторожность завязаться, скажем, на Java - и теперь не знает, как решить проблему масштабируемости. Но лучше сразу не привязываться к таким технологиям, проблемы которых можно снять только специальным железом.
Вы сами-то ее почитайте:
------------
Standard ECMA-262
------------
If the variable statement occurs inside a FunctionDeclaration, the variables are defined with function-local scope in that function, as described in s10.1.3. Otherwise, they are defined with global scope (that is, they are created as members of the global object, as described in 10.1.3) using property attributes {DontDelete}. Variables are created when the execution scope is entered. A Block does not define a new execution scope. Only Program and FunctionDeclaration produce a new scope.
------------
В "C" тоже есть глобальные переменные, проблема не в их наличии. Проблема в практике использования. Я не пишу на JavaScript подо все, что движется. Но когда я смотрю код HTML страниц с JavaScript и библиотек, которые используются при разработке сайтов - там переменная на переменной... Неудивительно, что память течет даже в Firefox при заходе на большие сайты.
Вот Вам пример - отсутствие поддержки сессий и транзакций в HTTP приводит к тому, что подвисший при загрузке на сервер мой комментарий опубликовался вместе с его новой редакцией, которую я послал позже увидев, что первое окно браузера висит и убив его.
Да отличный протокол, кто бы спорил - для своего времени вообще замечательный!
Только мир ушел далеко вперед с тех времен, когда нужно было просто потоком передать статическую страницу гипертекста с сервера на клиент. Теперь эта страница даже в стандартной модели представляет собой целую иерархию объектов, которые нужно грузить управляемо. Каналы стали такими быстрыми, а страницы такими большими, что TCP уже неадекватен для "реактивных" on-line приложений, как несущий протокол - один пакет где-то застревает и мы имеем задержку в секунды или дясятки секунд на всю страницу. Формально HTTP (как формат заголовков) к TCP отношения не имеет, но все же в стандарте явно указывается, что HTTP использует TCP и такова практика. Прокси и кэши для обеспечения работы миллионов клиентов должны быть как можно более быстрыми, в идеале даже аппаратными - а HTTP подразумевает полноценный парсинг [и переписывание] текстовых заголовков на промежуточных узлах. Концепция Web 2.0 предполагает, что пользователь интерактивно работает с сайтом - а в HTTP нет четкого понятия сессии и обратной связи с пользователем. Например, попробуйте написать на HTTP будильник или уведомить человека о приходе ему почты без поллинга, который создает траффик и жрет ресурсы Вашего сервера.
Да отличный протокол, кто бы спорил - для своего времени вообще замечательный!
Только мир ушел далеко вперед с тех времен, когда нужно было просто потоком передать статическую страницу гипертекста с сервера на клиент. Теперь эта страница даже в стандартной модели представляет собой целую иерархию объектов, которые не нужно грузить все сразу. Каналы стали такими быстрыми, а страницы такими большими, что TCP уже неадекватен для "реактивных" on-line приложений, как несущий протокол (хотя формально HTTP как формат заголовков к нему отношения не имеет, но все же в стандарте явно указывается, что HTTP использует TCP и такова практика). Прокси и кэши для обеспечения работы миллионов клиентов должны быть как можно более быстрыми, в идеале даже аппаратными - а HTTP подразумевает парсинг [и переписывание] текстовых заголовков на промежуточных узлах. Концепция Web 2.0 предполагает, что пользователь интерактивно работает с сайтом - а в HTTP нет четкого понятия сессии и обратной связи с пользователем.
Наличие явных указателей, как в C[++] предполагает как бы, что программист сам явно и отменяет память (не считая автоматической деаллокации объектов, созданных на стеке). Если же указателей нет, то это не обязательно в теории, но на практике всегда ведет к сборке мусора - нет явной деаллокации. Где сборка мусора - там проблемы с параллельностью. Программа в другом треде из машинного кода лезет в неизвестно какой блок памяти и мы не имеем над этим контроля. Или имеем контроль - но тогда мы уже отказываемся от прямой генерации нормального машинного кода и работаем с памятью через промежуточный слой и добавляем на каждое обращение еще несколько машинных команд. В результате - замедление вычислительно-емких или интенсивно обрабатывающих структуры данных программ в РАЗЫ. Решения проблемы требуют или использования особого железа, или же интеллектуального переключателя задач, специального компилятора и разметки машинного кода. Чего на практике мы не имеем.
Уже вложены миллиарды долларов в разработку байт-кода, JIT и всей технологии, связанной с Java. Зачем еще раз повторять вообще тоже самое, но с другим front-end языком? Не лучше ли вложить миллиарды долларов в что-то продвигающее технологии действительно вперед. Например, в те же функциональные языки, по типу Erlang и Haskell. Они теоретически могут вывести на новый уровень параллелизм программ. Или потратить усилия и деньги пусть не на новые технологии, но на что-то базисное, фундаментальное для написания остального софта. Например, создать новый язык системного программирования, который подходил бы для современных мульти-процессоров и заменил бы собой "C". Или вообще пойти в другую сторону, и поработать над технологиями, которые позволят создавать КАЧЕСТВЕННЫЕ и полностью динамические сайты без привлечения настоящих программистов. А зачем делать очередной JIT по типу предыдущего для языка, который ничего принципиально нового нам не даст?
Язык без указателей и при этом не тормозящий в мутьтитредной среде - это как философский камень. Кто из системных программистов не пытался найти решение проблемы такого аллокатора памяти? Но особых сдвигов нет за десятилетия - точнее решения не оправдывают себя, слишком много вносят накладных расходов. Есть только полу-меры, типа разных куч для объектов разных поколений или заказанных на разных процессорах и т.п. А покупать особое железо для своих серверов - это вообще путь в никуда. Во сколько Вам это обойдется, если понадобятся тысячи машин? Можете ли Вы рисковать своим бизнесом, завязываясь на одного поставщика уникального, ни с чем не совместимого железа? Это еще хуже, чем купить майнфрейм. :)
Я где-то противопоставляю "C" и JavaScript?
Речь вообще о другом - ЗАЧЕМ тратить деньги и время лучших программистов на ускорение исполнения JavaScript (как предположил автор доклада), то есть на вылизывание изживших себя, полностью превратившихся в нагромождение хаков старых технологий современного Web, если можно потратить эту энергию на разработку НОВЫХ ИДЕЙ!
Пусть будут новые языки - подходящие для параллельного программирования, и/или существенно упрощающие создание интерактивных распределенных приложений, в которых данные хранятся не на стороне клиента и весь контент динамический. Но ЗАЧЕМ тратить силы на доведение до блеска run-time сред от полностью погрязших в маразме и наслоениях старых систем?
Со стороны клиента автоматическая сборка мусора не вызывает никаких нареканий. Но на сервере из-за нее происходит трешинг кэшей процессора и задержки в обработке запросов (во время обхода кучи сборщиком мусора). Особенно непонятно, как совместить ее с мультитредностью нормально. Поэтому для быстродействующих приложений языки со сборкой мусора плохо подходят - ну разве что если запускать каждый их скрипт со своей кучей и уничтожать ее побыстрее, вместе со всеми данными. :)
Работать с внешними библиотеками (сложными) в C++ действительно очень сложно - согласен с Вами! Даже в чистом C сложновато, а в C++ зачастую попросту невозможно нормально совместить два разных больших пакета (со своей идеологией).
Интенсивное использование глобальных переменных и отсутствия block scope - первая (наипростейшая) из причин утечки памяти. А так же и то, и другое потворствует многим ошибкам и проблемам кода. В любом языке, где есть такая практика.
И каждый раз расходы времени серверов на генерацию human readable headers, а так же на парсинг запросов от клиента, особенно в AJAX. И баги в этом парсинге, в том числе дырки в security. :) А на proxy или в кэше - необходимость полного парсинга и rewrite headers. Но это как раз наименьшее из зол, а основное я перечислил ниже.
Но уже скоро их сверх-доходам придет конец, как это уже случилось со сверх-доходами у наземных телефонных операторов (в развитых странах).
Делается это так - когда кончается вся память в куче, аллокатор останавливает все треды программы средствами ОС (suspend thread). При этом ОС сохраняет контексты всех тредов (дампы регистров) в памяти. Так как типовая ОС (ни одна известная мне) не имеет вызова для массовой остановки тредов по списку, то останавливаем мы их в цикле. Вызов приостановки треда блокирующий и ждущий, так как иначе мы не были бы уверены, что thread остановлен и его регистры сохранены. В результате по мере остановки других тредов мы сами успеваем (как правило) потерять пару раз управление из-за переключения контекстов. Генерируется куча IPI на мультипроцессорной машине...
Наконец, все треды остановлены. Наш аллокатор запрашивает у ОС контексты всех тредов - так как ему нужно просканировать ВСЕ значения во ВСЕХ регистрах прикладной программы. Мы ищем в каждом из регистров значение, внешне ПОХОЖЕЕ на указатель на блок памяти, находящийся под контролем нашего аллокатора.
Когда находим такой "указатель" - маркируем соответствующий блок как зафиксированный, как и ВСЕ блоки, на которые он ссылается (для этого мы должны получить от компилятора или еще откуда-то описание всех структур данных в программе и рекурсивно обойти все блоки по ссылкам из данного блока).
Далее лезем во все стековые фреймы всех замороженных тредов, находим там все указатели на память и маркируем все блоки, на которые они ссылаются, как живые. И блоки на которые ссылаются эти блоки и т.д., рекурсивно. То есть, применяем классический mark-and-sweep алгоритм для разметки живых блоков и дальнейшего уничтожения мусора.
Блоки, которые маркированы как живые, но не помечены как зафиксированные - можно переместить, если мы в состоянии откорректировать ссылки на них. Это нужно если мы используем generational аллокатор. Без такого аллокатора вообще вся память при mark-and-sweep стратегии сборки мусора будет изведена в момент.
Перемещение блоков - отдельная песня, так как мы должны иметь возможность откорректировать все ссылки на перемещаемые блоки – значит нам нужно уметь трассировать их в обратную сторону.
Вот так это работает.
Требуется поддержка на уровне компилятора - но это не страшно, если у нас свой язык, а не C[++]. Очень большие накладные расходы и торможение в момент приостановки тредов - параллельность вся идет нафик. НО! Так как при использовании ХОРОШЕГО generational аллокатора сборка мусора происходит ОТНОСИТЕЛЬНО РЕДКО, то с этим можно мириться. Накладные расходы на поддержание структур тоже в целом приемлемы, на фоне выгод для программиста от языка без указателей.
Однако есть ВАЖНОЕ ограничение. Этот прием нормально работает (оправдывает себя) только с mark-and-sweep стратегией выявления мусора и подразумевает stopping the world - остановку всех тредов на время сборки.
Из-за этого данная стратегия, наиболее эффективная для исполнения кода на предельной скорости, не подходит для real-time приложений и для серверов, где нужно быстрое время отклика.
Так как в НЕПРЕДСКАЗУЕМЫЕ ЗАРАНЕЕ моменты система "зависает" на неопределенное по большому счету время и начинает собирать мусор. При этом НИ ОДНИН СОДЕРЖАТЕЛЬНЫЙ тред программы не может работать - все стоит раком. Сервер не отвечает на запросы, пока это не кончится, растут очереди. Далее все оживает и начинает снова работать быстро - до следующей сборки мусора...
Кэши все равно трешатся намного сильнее, чем в программах на C[++].
Но, тем не менее, несмотря на все эти минусы, ПРАКТИЧЕСКИ это лучший IMHO алгоритм. Несмотря на его идейную кривизну - например, на то, что иногда он случайное число в регистре какого-то треда ложно трактует как указатель на блок памяти. Все-таки это всего-лишь неэффективность, но не bug.
------------
Standard ECMA-262
------------
If the variable statement occurs inside a FunctionDeclaration, the variables are defined with function-local scope in that function, as described in s10.1.3. Otherwise, they are defined with global scope (that is, they are created as members of the global object, as described in 10.1.3) using property attributes {DontDelete}. Variables are created when the execution scope is entered. A Block does not define a new execution scope. Only Program and FunctionDeclaration produce a new scope.
------------
В "C" тоже есть глобальные переменные, проблема не в их наличии. Проблема в практике использования. Я не пишу на JavaScript подо все, что движется. Но когда я смотрю код HTML страниц с JavaScript и библиотек, которые используются при разработке сайтов - там переменная на переменной... Неудивительно, что память течет даже в Firefox при заходе на большие сайты.
Только мир ушел далеко вперед с тех времен, когда нужно было просто потоком передать статическую страницу гипертекста с сервера на клиент. Теперь эта страница даже в стандартной модели представляет собой целую иерархию объектов, которые нужно грузить управляемо. Каналы стали такими быстрыми, а страницы такими большими, что TCP уже неадекватен для "реактивных" on-line приложений, как несущий протокол - один пакет где-то застревает и мы имеем задержку в секунды или дясятки секунд на всю страницу. Формально HTTP (как формат заголовков) к TCP отношения не имеет, но все же в стандарте явно указывается, что HTTP использует TCP и такова практика. Прокси и кэши для обеспечения работы миллионов клиентов должны быть как можно более быстрыми, в идеале даже аппаратными - а HTTP подразумевает полноценный парсинг [и переписывание] текстовых заголовков на промежуточных узлах. Концепция Web 2.0 предполагает, что пользователь интерактивно работает с сайтом - а в HTTP нет четкого понятия сессии и обратной связи с пользователем. Например, попробуйте написать на HTTP будильник или уведомить человека о приходе ему почты без поллинга, который создает траффик и жрет ресурсы Вашего сервера.
Только мир ушел далеко вперед с тех времен, когда нужно было просто потоком передать статическую страницу гипертекста с сервера на клиент. Теперь эта страница даже в стандартной модели представляет собой целую иерархию объектов, которые не нужно грузить все сразу. Каналы стали такими быстрыми, а страницы такими большими, что TCP уже неадекватен для "реактивных" on-line приложений, как несущий протокол (хотя формально HTTP как формат заголовков к нему отношения не имеет, но все же в стандарте явно указывается, что HTTP использует TCP и такова практика). Прокси и кэши для обеспечения работы миллионов клиентов должны быть как можно более быстрыми, в идеале даже аппаратными - а HTTP подразумевает парсинг [и переписывание] текстовых заголовков на промежуточных узлах. Концепция Web 2.0 предполагает, что пользователь интерактивно работает с сайтом - а в HTTP нет четкого понятия сессии и обратной связи с пользователем.
Речь вообще о другом - ЗАЧЕМ тратить деньги и время лучших программистов на ускорение исполнения JavaScript (как предположил автор доклада), то есть на вылизывание изживших себя, полностью превратившихся в нагромождение хаков старых технологий современного Web, если можно потратить эту энергию на разработку НОВЫХ ИДЕЙ!
Пусть будут новые языки - подходящие для параллельного программирования, и/или существенно упрощающие создание интерактивных распределенных приложений, в которых данные хранятся не на стороне клиента и весь контент динамический. Но ЗАЧЕМ тратить силы на доведение до блеска run-time сред от полностью погрязших в маразме и наслоениях старых систем?