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

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]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 (ссылка)
В хаскелле классы совсем не то, что в ОО-языках.
То есть, класс - это открытое множество структур, для которых определены некоторые операции.

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

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

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


(Читать комментарии) -