Войти в систему

Home
    - Создать дневник
    - Написать в дневник
       - Подробный режим

LJ.Rossia.org
    - Новости сайта
    - Общие настройки
    - Sitemap
    - Оплата
    - ljr-fif

Редактировать...
    - Настройки
    - Список друзей
    - Дневник
    - Картинки
    - Пароль
    - Вид дневника

Сообщества

Настроить S2

Помощь
    - Забыли пароль?
    - FAQ
    - Тех. поддержка



Пишет kouzdra ([info]kouzdra)
@ 2009-06-02 14:34:00


Previous Entry  Add to memories!  Tell a Friend!  Next Entry
Entry tags:Компутерщина

О пресловутой "эффективности" С++:
К этой вот табличке (отсюда):

Я интереса ради сделал то, что советовали в комментах:

Написал вариант с кучей - с тремя вариантами - с auto_ptr, с shared_ptr "по-минимуму" и с shared-же ptr "по максимуму" - результат получился ожидаемый:

Time=2.19 (калибровочный исходный тест)
Time=41.44 (heap+auto_ptr)
Time=87.41 (heap+shared_ptr/min)
Time=83.87 (heap+shared_ptr/max)

Для калибровки еще Жаба:
Time=2.459 (inline вариант)
Time=9.026 (с классом complex)

Ну и OCaml (варианты из исходной статьи):
Time: 6.73479
Time: 8.0783
Time: 79.8885

И Haskell:

16.699

То есть мораль смешная - С++ ведет себя хорошо ровно до того момента, пока не начинается работа с кучей - и с этого момента он превращается в полную жопу даже по эффективности, даже по сравнению с O'Caml, у которого с плавающей арифметикой очень плохо, а объектность малоэффективна (там все методы виртуальные, причем диспатчатся они очень хитро, по причине структурного, а не именного subtyping'a
у объектов - вариант того, как такое надо писать на ML, если уж нужен офигенный полиморфизм - см. update).

Это, не говоря уж о потенциальной глючности и сложности кодирования плюсовой кучи.

Упреждая стандартный вопрос "зачем здесь куча" - отвечаю - здесь - ни зачем, но как только она понадобится - С++ начнет проигрывать всему, чему только можно (нет - есть конечно всякие аллокаторы, пулы и прочие финты ушами, которые более или менее вытянут
перформанс, но это уже гемморой)


import Complex

norm2 (x :+ y) = x*x+y*y

iters2 max_iter xc yc =
    let c = xc :+ yc in
    let aux count z = 
            if count >= max_iter 
            then max_iter 
            else
                if norm2 z >= 4.0 
                then count 
                else aux (count+1) (z * z + c)
    in
      aux 0 c

resolution = 5000
max_val = resolution / 2 
min_val = - max_val 
mul = 2 / max_val 
main = print (sum [iters2 100 (i * mul) (j * mul) | 
      i <- [min_val..max_val], j <- [min_val..max_val]])


Upd: И еще вариант на O'Caml с максимальным полиморфизмом,
но без ОО-жопы - в ML-style - с параметризоваными модулями - работает 17 секунд:

module type COMPL = 
  sig 
    type t
    type elem
    val to_float: elem -> float
    val of_float: float -> elem
    val make : re:elem -> im:elem -> t
    val add : t -> t -> t
    val mul : t -> t -> t
    val norm2 : t -> elem
  end

module Mand  (COMPL: COMPL) = struct

  let iters max_iter xc yc = 
    let c = COMPL.make xc yc in
    let rec aux count z = 
      if count >= max_iter then max_iter else begin
	if COMPL.norm2 z >= COMPL.of_float 4.0 then count else begin
          aux (count+1) (COMPL.add (COMPL.mul z z) c)
	end
      end in
    aux 0 c
end

module MandC = Mand (struct
  type elem = float
  type t = {re: elem; im:elem}
  let to_float x = x
  let of_float x = x
  let make ~re ~im = {re=re; im=im}
  let add a b = {re=a.re +. b.re; im=a.im +. b.im}
  let mul a b = {re=a.re *. b.re -. a.im *. b.im; 
		 im=a.re *. b.im +. a.im *. b.re}
  let norm2 z = z.re *. z.re +. z.im *. z.im
end)


Upd2: Справедливости ради, если сделать совсем кастомный аллокатор для класса, цифирь
начинает выглядеть так:

Time=2.2
Time=4.08
Time=49.42
Time=60.18

Но shared_ptr'ы все равно остаются ужасом и моральным террором.


       void * operator new (size_t size) {     
          if (free_list != NULL)                
            {                                    
              void * res = free_list;             
              free_list = *(void **)free_list;     
              return res;                           
            }                                        
            return ::operator new (size);             
       }                                               
       void operator delete (void * addr) {             
         if (addr == NULL) return;                       
         *(void **)addr = free_list;                      
          free_list = addr;                                
       }                                                    



(Добавить комментарий)


[info]ppkk
2009-06-02 16:47 (ссылка)
Это разговор о компиляторах, по-моему, а не о языках. То есть: смешение понятий.

Обсуждать в таком случае стоит конкретные компиляторы с неизменяемой версией (ну и на одной и той же платформе, наверное).

Ну и для обсирания Цепепе целесообразнее было бы все числа сделать классами, а не только комплексные (всё-таки в стандартном Цепепе есть поддержка комплексных чисел: пользоваться auto_ptr, но не <complex> — это уж слишком).

(Ответить) (Ветвь дискуссии)


[info]kouzdra
2009-06-02 16:52 (ссылка)
Это разговор о компиляторах, по-моему, а не о языках.

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

целесообразнее было бы все числа сделать классами

А в O'Caml и Haskell для float так и есть, кстати. Именно поэтому O'Caml сильно проседает на плавающей арифметике. Так что С++ я действительн дал некоторый бонус.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ppkk
2009-06-02 17:21 (ссылка)
через дизайн языка особенно не перепрыгнешь
Ту же сортировку (я не дрочу на сортировки, просто пример удобный, так что постараюсь о них рассуждать) в стандартных библиотеках по-разному реализуют, например. <complex> — это часть стандарта Цепепе. Почему её не считать дизайном?

А в языках низкого уровня (пусть даже наряду с высоким) через дизайн прыгать легко. В Цепепе, Паскале даже (я-таки занимаюсь малоперспективным и сомнительным программированием на Дельфах: впервые на них программировал на нынешней работе, может и в последний раз будет здесь же) это прыганье не требует особых усилий. В отличие от остальных перечисленных языков (Паскаль не был перечислен: боюсь, без "прыганья" результаты были бы грустными, так что даже не хочу тест писать), где действительно не прыгнешь (я для самообразования читаю книжку про Хаскел — я на середине [~230 стр.], на первых страницах ввели тип дерева парой строчек, теперь на четыре страницы объясняют, как можно подсчитать количество вершин).

я действительн дал некоторый бонус
Это такой же бонус, как сделать скидку на виртуализацию (JVM) и Яву сотоварищи запускать на машинах следующего поколения по сравнению с теми, где Цепепе.

В принципе, неиспользование встроенного ассемблера теми языками, что его позволяют использовать — это уже огромный подарок остальным. Дизайн-то позволяет его использовать (я на Turbo Pascal как-то написал продвинутый тетрис для 80386, где графика и обработка клавиатуры были на ассемблере; как написать его для современного программного окружения я не знаю, ибо он тонко следил за временем, что сейчас малореально [нужны права на использование RDTSC, отключение всяких экономий электроэнергии, монопольное ядро и что-то с графикой]: так что трудности переноса кода в будущее не столько в том, что код корявый; это я к тому, что в принципе, пусть даже на эмуляторе {тетрис-то и без эмулятора работает, хоть и коряво}, запускать это можно).

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2009-06-02 17:26 (ссылка)
— это часть стандарта Цепепе. Почему её не считать дизайном?

Да можно и заюзить (как и в O'Caml) - только ничего не изменится - как только возникает необходимость в размещении объектов в куче (а она в большинстве программ возникает) - С++ начинает тихо сливать воду.

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

С момента появления нормальных компиляторов (граница - где-то Watcom C), встроенный ассмеблер скорее посадит производительность, чем даст выигрых. Единственное исключение - работа со специальными командами, вроде MMX - но и то - оптимально завернуть их в inline-функции и пользоваться функциями (как оно в либах gcc и сделано)

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ppkk
2009-06-02 18:01 (ссылка)
в размещении объектов в куче
Это проблема компилятора и ОС, как я понимаю. В Дельфах 7, например, с большим количеством объектов (не более ~20000000, наверное) у меня программа сливалась, а откомпилировали в Дельфах 2007 тот же код — никаких проблем и с бОльшим количеством.
Ну, если ковыряться, то управления памятью, конечно: в Дельфах 7 все желающие управление памятью поменяли ещё до выхода Дельф 2007.
(Зато в Дельфах 2009 программа компилируется без особых замечаний, но не работает:)

встроенный ассмеблер скорее посадит производительность
Речь не о том, чтобы через строчку складывать переменные командой на ассемблере, а о написании всей простой процедуры на нём.

работа со специальными командами, вроде MMX
Ради отдельных команд лучше не заморачиваться, наверное, вообще. А разве не макросы для этого в Си удобно использовать для гарантии встроенности?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2009-06-02 19:02 (ссылка)
а о написании всей простой процедуры на нём.

Примерно с времен Watcom и бессмысленно - компилятор скорее всего сделает лучше.

Ради отдельных команд лучше не заморачиваться, наверное, вообще

Ну почему - векторные команды могут дать ускорение в разы на правильно подобранных задачах.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ppkk
2009-06-02 19:23 (ссылка)
компилятор скорее всего сделает лучше
Компилятору помешает, если ассемблер через строку используют с переменными, которые компилятор чёрт знает в каких регистрах или памяти хранит, каким-то ещё алгоритмам оптимизации тоже создаст препоны.
А простую и понятную процедуру целиком на ассемблере написать должно быть эффективнее, то же мандельбротство.

векторные команды могут дать ускорение в разы на правильно подобранных задачах
Там ад с тем, как они передаются в ассемблерный код. Если пара команд, то копирования-выравнивания будут дольше происходить.
А вот кусочек кода в виде процедурки — другое дело.
Даже CRC32 и POPCNT, наверное, лучше процедуркой оформлять (которая с циклом или типа того), а не вызывать с бухты-барахты к каким-то переменным.

(Ответить) (Уровень выше)


[info]blacklion.livejournal.com
2009-06-02 18:50 (ссылка)
Да нет - тут все как раз в основном языком определяется - через дизайн языка особенно не перепрыгнешь.
Аллокатор -- штука компиляторо-зависимая, а не языко-зависимая.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2009-06-02 18:58 (ссылка)
Не вполне - совсем хорошо все равно не выйдет. Одна из проблем - аллокатор нельзя инлайнить по дефолту - он обязан быть функцией, вторая - необходимость delete, третья - необходимость счетчиков ссылок, если хочется не заморачиваться вопросом совсем уж сильно.

То есть сделать получше можно (самое простое - написать аллокатор самому), хорошо все равно в общем случае не будет.

Но у меня немножко другой пойнт - существует куча народа, которая уверена, что С++ - "эффективный язык", а Жаба - "неэффективный" - до известной степени это оправдано (что первоначальные примеры показывают) - но попасть на инверсию по эффективности - раз плюнуть.

То есть и Жаба и функциональщина гораздо устойчивее к "писанию, как удобно" - разброс намного ниже.

(Ответить) (Уровень выше)


[info]alamar
2009-06-05 11:37 (ссылка)
В хаскелле классы совсем не то, что в ОО-языках.
То есть, класс - это открытое множество структур, для которых определены некоторые операции.

То есть "так и есть" не совсем справедливо, мне кажется.

Как там виртуальность операций обеспечивается на уровне кода, я не знаю, правда.

(Ответить) (Уровень выше)


[info]ketmar
2009-06-02 18:58 (ссылка)
более того: о конкретном компиляторе на конкретной ОС. только в посте ненавязчиво забыли рассказать, какой именно компилятор и какая ОС.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2009-06-02 19:00 (ссылка)
Компилятор - GCC 4.2, можете прогнать на других - будет забавно сравнить. Но тоже самое верно и по отношению к остальным языкам.

OS - линуксь, но OS к скорости распределения памяти в программах отношения практически не имеет.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ketmar
2009-06-02 19:04 (ссылка)
хм. конкретно к ядру, может, и не имеет (хотя и не факт, но, в принципе, можно считать несущественным). а вот к stdlib — ещё как имеет.

(Ответить) (Уровень выше)


[info]ketmar
2009-06-02 19:05 (ссылка)
алсо: я не защищаю цпп, просто выглядит несколько… странно — не указывать компилятор/ОС, когда они очевидно влияют на тесты.

(Ответить) (Уровень выше)


[info]ppkk
2009-06-02 19:04 (ссылка)
Ну, я это назвал "платформой". И вспомнил пример с плохим управлением памятью в Дельфах 7 стандартной комплектации.

Но как критика конкретно auto_ptr/share_ptr может и подходит отчасти.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ketmar
2009-06-02 19:09 (ссылка)
конкретно, конечно, в больших и толстых проектах производительности для всё равно пишут свои аллокаторы, заточеные под задачи.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ppkk
2009-06-02 19:34 (ссылка)
Ну и стандартный модуль для работы с комплексными числами, полагаю, разумно было бы использовать, следуя дизайну языка (ибо STL входит в дизайн, а <complex> — в STL, хоть ты это и не любишь).

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ketmar
2009-06-02 19:39 (ссылка)
от stl надо бежать подальше с дикими воплями ваще, ящитаю. обосновать не могу.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2009-06-02 19:44 (ссылка)
Во-во - я даже могу - С++ все-таки без кастомизации приводит к жопе. Свежий пример - чего они там в shared_ptr такого наворотили, что он в разы скорость сажает - счетчики ссылок - тормозной метод, но не настолько же.

Ну или вот практически пример: у меня в свое время радикальный выигрыш в скорости дало переписывание расширяемого вектора (функциональный аналог жабского ArrayList): деталь, которая ускорила все в разы была простой: не очень большого размера буфер (16 элеметов по умолчанию) резервируется статически. После чего количество обращений к куче за памятью сократилось на порядок (просто потому, что почти всегда хватает).

В Java, что характерно, эта оптимизация бессмысленна: она на скорость не влияет.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ketmar
2009-06-02 19:51 (ссылка)
>В Java, что характерно, эта оптимизация бессмысленна: она на скорость не влияет.
ORLY?! O_O в жабе настолько всё херово?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2009-06-02 19:52 (ссылка)
Напротив - все настолько хорошо :)

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ketmar
2009-06-02 19:53 (ссылка)
э… а каким фигом может быть «хорошо», если как не извращайся с выделением памяти, скорость всё равно не увеличивается? мне кажется, это не «у жабы классно память сделана», а «жаба настолько угрёбищна, что даже трюки с облегчением работы memory manager ей не помогают». %-)

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2009-06-02 19:56 (ссылка)
если как не извращайся с выделением памяти, скорость всё равно не увеличивается?

А ей некуда увеличиваться. Затраты с тем, что "все в куче" там (как и в ML) не на выделение памяти, а на лишние косвенности и всякие vmt внутри объектов. А оптимизить саму операцию new там действително практически бессмысленно.

(Ответить) (Уровень выше)


[info]ketmar
2009-06-02 19:52 (ссылка)
я-то не могу просто потому, что не подружился с stl с самого начала. соответственно, маловато «инсайда» и тестов, чтобы обоснованно ругаться.

(Ответить) (Уровень выше)


[info]q
2009-06-02 23:17 (ссылка)
чего они там в shared_ptr такого наворотили

Ясно чего.

shared_ptr - он прозрачен для работы с нитками, то есть чтение-копирование - атомарны. Следовательно, работа со счётчиком ссылок по дефолту обвязывается мутексами.

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

Кстати, в boost (откуда он и взят чуть менее, чем целиком) имеется несколько дефайнов времени компиляции для ускорения работы; среди них - дефайн "ниток не будет, мутексов не надо".

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2009-06-02 23:58 (ссылка)
shared_ptr - он прозрачен для работы с нитками, то есть чтение-копирование - атомарны. Следовательно, работа со счётчиком ссылок по дефолту обвязывается мутексами

Кстати - к вопросу о "функциональных языках" - почти все любители попинать "высокую функциональную идею" не допирают например до простой вещи - чисто функциональные структуры данных не нужнаются в mutex'aх. Что в мультитредных приложениях обычно с лихвой перекрывает издержки на их организацию (которые выражаются не слишком большим константным множителем). Не говоря уж о радикальном упрощении логики.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]max630.livejournal.com
2009-06-03 08:03 (ссылка)
ну зачем так передёргивать, мы же говорим о потрохах умных указателей, вы таки хотите сказать что сборка мусора для мультитредных программ обходится без мутексов?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2009-06-03 13:33 (ссылка)
Сборщик мусора - операция сравнительно редка и выполняющаяся сравнительно крупными блоками. А тут, если например надо по дереву пробежать, надо либо лочить все дерево (что может привести к проседанию UI, либо на каждый переход по ссылке, что - тормоза, либо извращаться чтобы и не слишком часто, и не слишком редко.

(Ответить) (Уровень выше)


[info]q
2009-06-03 08:38 (ссылка)
Структуры данных, использующие shared_ptr, тоже не нуждаются в мутексах. Мутексы - они внутре.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2009-06-03 13:31 (ссылка)
Так это и значит, что нуждаются. Но на самом деле - нуждаются и вовне - потому что целостность структуры в момент работы с ней надо обеспечивать.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]q
2009-06-03 15:23 (ссылка)
Ну так если виртуальная машина функционального языка будет использовать внутри себя ленивое копирование в каком-либо виде, то и там нужны будут мутексы, чудес не бывает.

А если копирование честное, то никакой shared_ptr не будет использоваться, и мутексы не будут нужны и в C++.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2009-06-03 15:30 (ссылка)
Ну так если виртуальная машина функционального языка

Там нет обычно никаких виртуальных машин. Это не Java же. И при чем тут "копирование"? Копировать ссылку на неизменяемую структуру можно сколько угодно без всяких мутексов.

Мутексы при обходе дерева, например, нужны потому, что иначе нельзя гарантировать, что при проходе по ссылке она будет валидна - хоть в каком-то виде - например я могу проверить ее на !=NULL, а к моменту ее выборки (пусть это даже следующая команда), она уже будет обнулена другим тредом. Искать такие ошибки, кстати, очень трудно.

Если дерево не мутирует - не нужны и мутексы. К функциональым языка это не имеет отношения (хотя для них это естественный стиль) - immutable структуры можно и на C писать - зато относится к pure functional data structures. И к сборке мусора, потому что без сборки мусора их программировать крайне трудно, да и использовение ref.counts убивает сам смысл затеи - ибо структура оказывается мутирующей из-за этих самых счетчиков.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]sasha_gil
2009-06-03 18:44 (ссылка)
>>>И к сборке мусора, потому что без сборки мусора их программировать крайне трудно, да и использовение ref.counts убивает сам смысл затеи - ибо структура оказывается мутирующей из-за этих самых счетчиков.

Тут я бы поспорил: а) использование везде смартпойнтеров, скрывающих рефкаунт-возню, действительно здорово упрощает программирование конкретной прикладной задачи (сами смартпойнтеры аккуратно запрограммировать -- это труд, но он делается один раз и можно взять уже запрограммированные, как ты в этом примере) и б) плохие рэйс кондишены возникают из-за логической мутируемости структуры. "Физическая" мутируемость из-за рефкаунтов логическую мутируемость не ломает, проблемы не создаёт. Кстати не понял про необходимость мьютексов -- под виндой я пользуюсь вроде бы более лёгкими InterlockedIncrement / Decrement, такого в Линуксе что ли нет?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]q
2009-06-03 21:40 (ссылка)
Под линуксом есть много чего, в том числе и политика сборки boost совсем без мутексов, но полностью корректными (особенно на многопроцессорных системах) могут быть только настоящие, в случае линукса - libpthread. Это довольно тонкий вопрос, в котором легко сделать ошибку.

Про применимость InterlockedIncrement / Decrement ничего не могу сказать, не знаю.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]max630.livejournal.com
2009-06-03 23:50 (ссылка)
выбор там такой:

static const _Lock_policy __default_lock_policy =
#ifdef __GTHREADS
#if (defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_2) \
&& defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_4))
_S_atomic;
#else
_S_mutex;
#endif
#else
_S_single;
#endif

и вот сейчас оно использует _S_atomic, то есть это не мутекс, как я понимаю.

я переткнул на _S_single и оно особо не поменялось.

(Ответить) (Уровень выше)


[info]ppkk
2009-06-05 22:01 (ссылка)
Если я правильно понял, то для реализации потенциальных преимуществ "функциональных языков" по скорости выполнения давно придуманы SSA/SSU оптимизации.

Понятное дело, что всё равно придётся программировать.

Уже тот факт, что быстродействие у части программы без "побочных эффектов" (RESOLUTION не вводится пользователем, например, не считывается из файла), означает, что от оптимизирующего компилятора не ожидают совершенно естественного и правильного решения: забить на весь код и просто подставить ответ.

И ещё о сборке мусора: если это законченная задача (выдаёт числовой ответ), а не часть сложной программы, то сборка мусора очень тупо реализуется средствами ОС. То есть: истекающая памятью программка собирается в исполняемый файл, который выдаёт ответ и завершает работу. Всё: система сама приберётся. А на возражения можно ответить тем, что тест должен быть адекватным: например, чтобы без заботы о памяти программа просто не могла выполнить работу.

(Ответить) (Уровень выше)


[info]i.dluciv.name
2009-06-02 17:04 (ссылка)
Хе, а уж когда начинают вызываться виртуальные методы, особенно, если множественное наследование было, то вообще свет туши. Остается далеко позади.

.NET и Java их инлайнят время от времени, плюсы - в принципе нет, так как компиляция раздельная.

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

(Ответить)


[info]vkni
2009-06-02 18:32 (ссылка)
Спасибо. С одной стороны познавательно, с другой - больше удивляет наличие языков без проседания по производительности при выделении памяти в куче.

А какие именно отличия в дизайне от C++ позволяют реализовывать такую быструю операцию new в Java?

Насчёт задач с объектами в куче в внутреннем цикле - я боюсь, что если ты протестируешь современный фортран, то увидишь, что он тоже просядет. Только нужно тестировать не gcc, а ifc или Lahey и т.д. И если они просядут, значит таких вычислительных задач очень мало.

(Ответить) (Ветвь дискуссии)


[info]ppkk
2009-06-02 19:13 (ссылка)
Разве Ява вообще работает с той же кучей?

Си, как я понимаю, выделяет память средствами ОС.

Ява до последнего времени (не пользуюсь особо) использовала на пользовательской машине только заранее прописанный объём памяти, динамически её вообще не выделяя (например, 64 Мб виртуальной машине, а сколько на компьютере стоит — роли не играет). Если эту убогую проблему решили, то, как я понимаю, она всё равно выделяет виртуальной машине память большими кусками, внутри которых сама ей управляет.

Ну и если написать кривую виртуальную машину, то все числа изменятся, а вот дизайн останется тем же.

А в Цепепе, как и написал [info]kouzdra, тоже можно выкрутиться с помощью:"…есть конечно всякие аллокаторы, пулы и пр.".

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]kouzdra
2009-06-02 19:54 (ссылка)
Си, как я понимаю, выделяет память средствами ОС.

Не в большей степени, чем Java - берет себе кусок, по мере необходимости расширяет. Но управление памятью внутри программы.

Ява до последнего времени (не пользуюсь особо) использовала на пользовательской машине только заранее прописанный объём памяти, динамически её вообще не выделяя

Она берет столько, сколько сказано, как минимум, и потом добирает по потребности, но не больше лимита.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]vkni
2009-06-02 19:57 (ссылка)
А из-за каких проблем в C++ нельзя сделать такой же чудный аллокатор, как в Java?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]joppux
2009-06-03 03:49 (ссылка)
Хотя бы из-за того, что очень трудно сделать копирующий сборщик мусора - он будет неэффективен из-за того, что нельзя различить int и void* - придется добавлять уровень косвенности.

(Ответить) (Уровень выше)


[info]ppkk
2009-06-02 20:27 (ссылка)
но не больше лимита
И лимит до сих пор по умолчанию не связан с наличием памяти на машине, а прописан в 64 или 128 Мб? Я не в курсе (я про изначальные настройки пользовательской виртуальной машины). Помню, что обсуждавшееся ?год назад? сравнение быстродействия у меня не заработало.

Не в большей степени, чем Java
Да, это я несколько преувеличил.
Но в дизайне различие большое: для программы на Цепепе под Окна вызвать функцию Windows API для выделения памяти — нормально, а на Яве — хакерство, которое необходимо запретить.

(Ответить) (Уровень выше)


[info]vkni
2009-06-02 19:56 (ссылка)
> Си, как я понимаю, выделяет память средствами ОС.

Насколько я понимаю, нет - там есть буферизация. Т.е. malloc выделяет память из внутренней кучи, которую glibc запрашивает у системы. Причём реализация разнится у gcc+GNU/Linux и msvc+Windows(NT).

Некоторое время назад из-за этого были проблемы у firefox:

http://mr.himki.net/index-alloc.html

http://www.linux.org.ru/view-message.jsp?msgid=2262005

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ppkk
2009-06-02 20:35 (ссылка)
там есть буферизация
Типа того.

Но дизайн Цепепе совершенно не мешает вызывать функции ОС для управления памятью, в отличие от дизайна Явы.

Я для Микрософта попытался понять детали — не понял. Да и стандарта Цепепе у меня нет (его надо искать в интернете: на халяву в особо видных местах не выложен).

Некоторое время назад из-за этого были проблемы у firefox
Ну, здесь уже ограничение дизайна: если не предусмотрена возможность перемещать куски выделенной памяти незаметно для использующей её программы, то что-нибудь такое неизбежно.
Вот с таким ограничением дизайна можно согласиться, наверное.

(Ответить) (Уровень выше)


[info]kouzdra
2009-06-02 19:59 (ссылка)
Просто, поскольку куча по определению интенсивно используется - работа с ней сделана эффективно. За жабу не скажу, а O'Caml например, кроме прочего еще и кластеризует последовательные запросы к куче - на x86 выделение блока - 5 команд, причем с большой вероятностью будет размещено сразу несколько объектов.

Ну и всякие радости от gc - что не надо delete делать явно, что нету возни со счетчиками ссылок etc etc.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]vkni
2009-06-02 20:05 (ссылка)
Но тогда никто не мешает сделать кучу в C++ тоже эффективной? В смысле malloc/new/free/delete.

Просто стиль программирования такой, что тормоза в malloc/new/free/delete везде обходятся стороной.

Ну и конечно встроенный gc всегда будет быстрее всяких shared_ptr.

----------------

А есть возможность проверить фортран?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ppkk
2009-06-02 20:29 (ссылка)
Но тогда никто не мешает сделать кучу в C++ тоже эффективной?
Вот именно. Тормоза не с дизайном связаны, а с желанием создать тормоза. И стандартный <complex> не использован.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]vkni
2009-06-02 20:35 (ссылка)
Видите ли Пётр, во внутренних циклах выделение памяти из кучи в С++ обычно не делают. Поэтому в обсуждаемой ситуации есть немного от неуловимого Джо.

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ppkk
2009-06-02 21:25 (ссылка)
во внутренних циклах выделение памяти из кучи в С++ обычно не делают. Поэтому в обсуждаемой ситуации есть немного от неуловимого Джо
Не понял, почему такой тон, а выделение памяти из кучи во внутренних циклах делают. Например, если это не комплексные числа (для которых, на что никто не обращает внимание, дизайном, стандартом, чем угодно этого языка давно предусмотрены средства), а менее известного размера небольшие объекты (хотя бы строки существенно разной длины).
Я несколько раз сталкивался с неизбежной необходимостью (на другом языке) подстраивать динамическое выделение памяти программой под особенности управления памятью реализации языка (то есть ограничивал меня не дизайн, а именно реализация: в других версиях и результаты отличались). Для тех же строк во внутренних циклах, когда не было необходимости их редактировать, дошёл даже до выделения большого куска памяти и самостоятельного (ибо особо простой случай) управления ею.

Разъясню свою позицию, раз уж такие дела:
1. Быстродействие в этих ситуациях в бОльшей мере вопрос не дизайна языка, а его реализации (компилятора, интерпретатора и виртуальной машины, если требуется), причём возможно с большой зависимостью от ОС.
2. У Явы (или .Net) нет таких указателей, ограничивающих эффективное управление памятью. У них существенно другие кучи, чем в Цепепе.
3. Но дизайн языков с компиляторами не мешает написать своё (или взять готовое) управление памятью, пользоваться им без указателей адреса памяти и т.п., достигая быстродействия, идентичного явовскому.
4. В примерах не использованы нестандартные средства работы с комплексными числами, что уж совсем как-то вот слишком.
5. Быстродействие auto_ptr и shared_ptr [Boost, в отличие от <complex>, например, и auto_ptr пока не является частью стандартного Цепепе] действительно поставлено под сомнение, но применены они не по назначению. Разве используется RAII? Или множественное владение shared_ptr?
6. Если уж Boost, то надо было использовать scoped_ptr (он быстрее). Но и его пользовать смысла нет.
7. Ясно, что особо хитрый оптимизирующий компилятор Цепепе, хорошо рассчитанный на auto_ptr/shared_ptr теоретически мог бы справиться с кодом [info]kouzdra и обойтись вообще статическим выделением ячеек памяти, количество которых ясно из max_iter.

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

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]vkni
2009-06-02 23:29 (ссылка)
> Не понял, почему такой тон, а выделение памяти из кучи во внутренних циклах делают.

Забыл :-) поставить.

(Ответить) (Уровень выше)


[info]nicity.livejournal.com
2009-06-02 21:08 (ссылка)
Программу на Джаве скорее всего тоже можно сделать быстрее, если завести список свободных объектов (по типу new / delete), пробовал десять лет назад :) Кстати, хорошо бы попробовать померить C++ с gc :)

(Ответить)


[info]ppkk
2009-06-02 21:20 (ссылка)
А scoped_ptr?

(Ответить) (Ветвь дискуссии)


[info]max630.livejournal.com
2009-06-03 08:23 (ссылка)
А как его тут применить? scoped_ptr нельзя вернуть.

И вообще смысл? Там 2 источника жопы: 1. деструкторы, 2. необходимость явно сказать delete каждому освободаемому куску памяти. С правильным GC ни того ни другого нет. Хотя есть ли там вообще выделение памяти (http://lj.rossia.org/users/kouzdra/664091.html?thread=4255771#t4255771)?

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]ppkk
2009-06-03 17:22 (ссылка)
Там, вроде бы, дополнение ("снимаю вопрос").

Я не знаток Цепепе, тем более Boost-а, прочёл, что scoped_ptr проще и быстрее, вот и спросил.

Но auto_ptr (стандарт) и shared_ptr (Boost) — это явно не просто указатели с подсчётом ссылок. Там же RAII и что-то ещё сложнее.

А с простым (ну, при присваивании тоже хитрости есть, правда) подсчётом ссылок в Паскале современном (FreePascal, Дельфы может) используются строки и динамические массивы: там быстродействие очень сильно зависит от реализации. Тоже можно считать автоматической сборкой мусора.

В то же время, как я уже писал, "хороший" оптимизирующий компилятор Цепепе, заточенный под стандартные указатели (если shared_ptr станет стандартным) мог бы отлавливать ситуации, когда не используется бОльшая часть возможностей и всё упрощать (например, статически выделить память под константу, передаваемую в max_iter): дизайну языка это бы не противоречило (ну, раздельная компиляция, конечно, может мешать, но современные-то компиляторы Цепепе используют межпроцедурную оптимизацию).

(Ответить) (Уровень выше)


[info]max630.livejournal.com
2009-06-03 00:03 (ссылка)
время для java, ocaml и haskell включает сборку мусора?

(Ответить) (Ветвь дискуссии)


[info]kouzdra
2009-06-03 00:04 (ссылка)
Конечно - но на этих тестах это исчезающе малая величина. Со сборкой мусора вообще есть много предрассудков - например, что явный new/delete эффективнее. Как правило - нет.

(Ответить) (Уровень выше)


[info]max630.livejournal.com
2009-06-03 07:50 (ссылка)
А где код для haskell?

(Ответить)


[info]max630.livejournal.com
2009-06-03 08:06 (ссылка)
Я вот смотрю на альтернативные варианты и не вижу, где там куча. Собственно, это конечно большое достоинство высокоуровневых языков - тишком для юзера в зависимости от ситуации применять аллокацию на стеке или в куче.

(Ответить) (Ветвь дискуссии)


[info]max630.livejournal.com
2009-06-03 10:29 (ссылка)
ага, если верить Gc, есть, этот вопрос снимаю

(Ответить) (Уровень выше)


[info]kouzdra
2009-06-03 13:34 (ссылка)
Много где - напримел float в O'Caml в куче, если компилятор не соптимизит локально. Полиморфизм же на том и держится, что все влезает в слово.

(Ответить) (Уровень выше)


[info]max630.livejournal.com
2009-06-03 11:49 (ссылка)
Собсно, для большинства задач сейчас процессора хватает не то что на c++, а даже для питонов. БОльшие вопросы, например, к яве, возникают про потребление памяти. К ocaml и haskell вопросов с быстродействием вроде и не было (хотя с haskell есть где поебаться, если повезёт).

(Ответить) (Ветвь дискуссии)


[info]kouzdra
2009-06-03 13:43 (ссылка)
для большинства задач сейчас процессора хватает не то что на c++, а даже для питонов

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

(Ответить) (Уровень выше) (Ветвь дискуссии)


[info]max630.livejournal.com
2009-06-03 20:43 (ссылка)
Первый раз слышу. Из всех флеймов perl vs php тема быстроты не возникала ни разу.

(Ответить) (Уровень выше)

скрестил с libgc
[info]max630.livejournal.com
2009-06-04 18:06 (ссылка)
Получилось неожиданно хорошо

http://www2.max630.info/bench/main.cpp


result: 290068052
Time=2.25
result: 290068052
Time=57.37
result: 290068052
Time=131.66
result: 290068052
Time=142.14
result: 290068052
Time=55.03 -- это с явным удалением
result: 290068052
Time=36.06</strong>
result: 290068052
Time=54.13 -- это тоже с явным удалением, но используется gc-шный аллокатор

(Ответить)


[info]ppkk
2009-06-05 21:51 (ссылка)
MS Visual Studio 2008, все оптимизации, что нашёл, включены:

3,89
239,922
593,906
671,859

Если в долях, то числа из заметки:
1,00-18,92-39,91-38,30

А у меня:
1,00-61,68-152,68-172,71

(Изменения в программе: используется скачанный и установленный Boost 1.39, и ради clock_t подключена библиотека Time.h, также убрана какая-то странная функция со словом "asm", так сказать.)

Так что очевидна и существенная зависимость от реализации или платформы.

Хотел написать простейшие программки на FreePascal-е, с динамическими массивами (которые с подсчётом ссылок), но уж больно искусственные эти тесты, забил на это дело.

(Ответить)

Ох уж эти мне gc-тролли
[info]legolegs.livejournal.com
2009-07-09 17:05 (ссылка)
Не могу пройти мимо. Это спорт такой - "найди где плюсы быстрее и затормози"?

Основное преимущество плюсов перед этими интерпретируемыми поделками - это принцип "ты не платишь за то, что не используешь". Нет виртуального наследования - нет vtable. Не нужна куча - работаем на стеке с _любыми_ объектами (а только с POD, как у некоторых). Вместо прикручивания *_ptr'ов к плюсам, стоило бы _открутить_ кучу от окамла и скалы. Слабо?

И ещё один момент: Конечно, это очень весело - доказывать бенчмарками, что gc не тормозит. Только не исключено что он тупо не успел начать тормозить. Если бы программа работала хотябы полчасика - другая картина бы была. Ну или в варианте на плюсах сделать new и не делать delete (как советовали выше) - тоже справедливо получится.

(Ответить)

без темы
(Анонимно)
2010-06-03 04:54 (ссылка)
код на С++ из разряда вырви глаз и выкинь нахуй...
убил бы того кто это написал...
а вы знаете что если поставить после каждой инструкции в С++ цикл до миллиона, то он тоже начнет всем сливать, вот только если этот же цикл присунуть жабе она хуй запустится...
как заебали эти долбоебы, которые вечно пытаются затормозить С++... нахуя?? лучше ускорьте свои жабы и окамлы!

(Ответить)