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

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]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 и оно особо не поменялось.

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


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