boost::shared_ptr, GC и тормоза

топ 100 блогов love5an26.03.2011 Я на самом деле очень сильно удивлен тем, что очень много людей даже примерно не представляют себе, сколько оверхеда вносят счетчики ссылок, и насколько они менее эффективны, чем нормальный сборщик мусора.

Не перестают меня удивлять и заявления о том, что де, раз в C++ есть boost, с его shared_ptr и прочим, то проблема сборки мусора для C++ фактически решена.

Ладно уж, не будем о том, что счетчики ссылок неспособны разрешить циклические зависимости, просто сравним ту самую производительность, за которую писатели на C++ так трясутся.

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

Заключается он в следующем:
Создаем вектор на 'length' элементов, заполняем его указателями на структуры, подобные лисповым cons-ячейкам, и 'n' раз прогоняем цикл, в котором создаем копию этого вектора, а старый удаляем(элементы при этом мы не копируем, а копируем только указатели на них). Указатели, в C++, естественно, используем "умные".
В качестве противника C++, то есть в качестве языка, в реализациях которого присутствует нормальный GC, я взял Common Lisp, если конкретно - SBCL.


Для варианта на Common Lisp получился вот такой код:

(declaim (optimize (speed 3) (safety 0)))

(defun copy-vector (vector)
  (declare (type simple-vector vector))
  (let* ((length (length vector))
         (new-vector (make-array length)))
    (dotimes (i length)
      (setf (svref new-vector i) (svref vector i)))
    new-vector))

(defun test (n length)
  (declare (type fixnum length n))
  (let ((vector (make-array length)))
    (dotimes (i length)
      (setf (svref vector i) (cons nil nil)))
    (dotimes (i n)
      (setf vector (copy-vector vector)))))

(defun main (&aux (n (second sb-ext:*posix-argv*))
                  (length (third sb-ext:*posix-argv*)))
  (when (> (length sb-ext:*posix-argv*) 3)
    (write-line "Usage test_sbcl [ n [ length ] ]")
    (sb-ext:quit :unix-status 1))
  (let ((n (or (and n (parse-integer n))
               1000000))
        (length (or (and length (parse-integer length))
                    100)))
    (declare (type fixnum n length))
    (test n length))
  (sb-ext:gc :full t))

(sb-ext:save-lisp-and-die "test_sbcl.exe"
                          :executable t
                          :toplevel #'main)


Обращаю внимание(чтобы не было возмущений на тему того, что в лиспе, в отличие от варианта на C++, память не освобождается), что в конце функции main я все-таки вызываю сборщик мусора SBCL, в параметром :full t, что означает, что он проводит "полную" сборку мусора, освобождая все объекты, которые только можно.

Для C++ получилось вот так:

#include 
#include 
#include 
#include 

struct Cons
{
    void* car;
    void* cdr;
    Cons(void* _car, void* _cdr)
    {
        car = _car;
        cdr = _cdr;
    }
};

typedef boost::shared_ptr ConsPtr;
typedef std::vector ConsPtrVector;
typedef boost::shared_ptr ConsPtrVectorPtr;

ConsPtrVectorPtr CopyVector(ConsPtrVectorPtr vec)
{
    int length = (*vec).size();
    ConsPtrVectorPtr copy(new ConsPtrVector(length));
    for(int i = 0; i<= 0)
            {
                std::cout << "Invalid parameter: " << argv[1] << std::endl;
                return 1;
            }
            break;
        case 3:
            if((n = atoi(argv[1])) <= 0)
            {
                std::cout << "Invalid parameter: " << argv[1] << std::endl;
                return 1;
            }
            if((length = atoi(argv[2])) <= 0)
            {
                std::cout << "Invalid parameter: " << argv[2] << std::endl;
                return 1;
            }
            break;
        default:
            std::cout << "Usage: test_cpp [ n [ length ] ]" << std::endl;
            return 1;
    }
    Test(n, length);
    return 0;
}


Собирается вариант на лиспе вот такой командой:
sbcl --script test.lisp

А вариант на C++, соответственно, вот такой:
g++ -O3 -o test_cpp.exe -I/d/boost_1_46_1/ test.cpp
(я использую MSYS и GCC из mingw32)

Результаты, если честно, меня удивили. Ну то есть, я предполагал, что вариант со счетчиком ссылок будет медленнее, но чтобы настолько!
boost::shared_ptr, GC и тормоза
Как видно, программа на лиспе, с его сборщиком мусора, отрабатывает быстрее аж более чем в 4.5 раза(~0.6 секунд против ~2.8 секунд для C++). И это еще учитывая что CL - высокоуровневый язык с динамической типизацией, и учитывая всю инициализацию, которую проводит рантайм лисп-системы при запуске.

Ну ладно, думаю, всем известно что GC идеально подходит для множества выделений мелких объектов(а в том варианте цикл прогоняется 1000000 раз, с размером вектора в 100 элементов), надо посмотреть как C++ себя покажет при работе с крупным массивом.
boost::shared_ptr, GC и тормоза
Ну что сказать - отрыв в 3.2 раза(~1.7 секунд против ~5.4) это, конечно, не в 4.5, но где-то рядом :)

Результат, в принципе, становится понятным из ассемблерного кода.
Ну то есть, в случае с SBCL я предполагал, что он мне там нагенерирует, примерно:
http://paste.lisp.org/display/120909
А вот лапшу, в которую вылился код на C++ - я разобрать так и не смог:
http://paste.lisp.org/display/120910
Кто-то еще говорит, что он понимает, во что превратится его код после обработки оптимизирующим компилятором?

Оставить комментарий

Архив записей в блогах:
Глава Одесской области обвинил правительство Украины в регрессе экономики. По его словам, Порошенко добился того, что за время его правления страна деградировала и, чтобы восстановится до прежнего уровня, понадобится, как минимум, 15 лет. Так же губернатор порадовал ярким сравнением ...
Многие, почитав мои позитивные бесконечно обличительные правдивые посты про столичную жизнь, тут же восклицают: а у нас в Урюпинске/Мухосранске/Пердиловке всё не так! Тут же начинают завидовать и клясть зажравшуюся офисную массу. Но не всё так однозначно, уверяю вас. И для подтверждения пр ...
Здравствуйте уважаемые. Очередной пост про известных кутюрье, которые разрабатывали костюмы для тех или иных кинокартин. В прошлый раз вспоминали Готье: https://id77.livejournal.com/5240591.html , ну а сегодня пару слов следует сказать о великом и могучем Джордже Армани. Думаю, все знают ...
Пока не закончился месяц, поделюсь своими июньскими календарными страницами... В моём представлении июнь — лёгкий летний месяц, полный золотой зелени, прозрачного света, белых ночей... Лёгкость и свет - вот июнь... Календарь настенный, А3, мой любимый А.А. Дейнека: Календарь ...
Пока мы в НИИ обсуждаем семейные дрязги Сирожи и Димы, потуги недогурков типа кунгура и Круза Кастильо , Батько всея Беларуси в стремлении построить коммунизм в отдельно взятой стране, вводит крепостное право для работников госпредприятий. ...