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

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 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
Ну, здесь уже ограничение дизайна: если не предусмотрена возможность перемещать куски выделенной памяти незаметно для использующей её программы, то что-нибудь такое неизбежно.
Вот с таким ограничением дизайна можно согласиться, наверное.

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


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