LINUX.ORG.RU

Сравнение производительности доступа к полям структур в Python, Common Lisp и С++

 , cоmmon lisp, , ,


0

2

Помните, я сказал, что постараюсь не писать до конца сентября? Так вот: у меня не получилось :) Кому не нравится тут тег Яр - заверяю, что он не для рекламы (и так все уже знают), а исключительно для удобства: мне по нему удобно искать, а вам его удобно заигнорить. Отношение к Яру здесь такое: я мучаюсь, как мне реализовать объекты Яра. Склоняюсь к тому, что сделать их с опцией «реализовать как defstruct/clos». Но дальше идут тонкости, в которые сейчас не лезу.

Итак, я нашёл тред пиписькометрии 7 - летней давности: https://www.linux.org.ru/forum/development/4535326, и там сравнивалась скорость каких-то циклов. но это неинтересно. Интересно сравнивать скорость доступа к полям объектов хотя бы. Я попробовал. Вот что в итоге вышло (платформа 64 разрядная).

С++

// cpp-benchmark.cpp
#include "stdio.h"
#include <cstdlib>

class test_class {
public:
	int fld1, fld2, fld3, fld4, fld5;
        test_class *next;
};


int inner(test_class *o,int bound) {
    int res=0;
    for (int i=0;i<bound;i++) {
        res += o->fld1;
        o->fld2 = o->fld1;
        o->fld3 = o->fld2;
        o->fld4 = o->fld3;
        o->fld5 = o->fld4;
        o->fld1 = res - 1;
        o = o->next;
        res = res % 16;

    }    
    return res;
}

int main(int argc, char* argv[])
{
    test_class o1;
    test_class o2;
    o1.fld1=1;
    o2.fld1=1;
    o1.next=&o2;
    o2.next=&o1;
    int n = 100*1000*1000;
    int result=inner(&o1,n);
    printf("%d %d\n",o1.fld5,result); // проверяем корректность и чтобы оптимизатор
    // не выкинул неиспользуемый код
    return 0;
}
// EOF
// запускаем:
g++ -O2 -o cpp-benchmark cpp-benchmark.cpp ; echo disassemble inner | gdb cpp-benchmark ; time ./cpp-benchmark 
// листинг пропускаю.

real	0m0.225s
user	0m0.216s
sys	0m0.004s

Лисп на структурах:

;; struct-benchmark.lisp
(in-package :cl-user)

(list
 '#.(restrict-compiler-policy 'safety)
 '#.(restrict-compiler-policy 'debug))
 
(declaim
 (optimize (speed 3) (safety 0) (debug 0)
           (space 0) (compilation-speed 0)))

(defparameter *times* (* 100 1000 1000))

(defstruct test-struct 
  (next nil :type (or null test-struct))
  (fld1 0 :type fixnum)
  (fld2 0 :type fixnum)
  (fld3 0 :type fixnum)
  (fld4 0 :type fixnum)
  (fld5 0 :type fixnum)
  )

(declaim (inline test-struct-fld1 test-struct-fld2 test-struct-fld3 test-struct-fld4 test-struct-fld5 (setf test-struct-fld1) (setf test-struct-fld2) (setf test-struct-fld3) (setf test-struct-fld4) (setf test-struct-fld5)
                    test-struct-next))

(defun inner (o n)
  (declare (type test-struct o))
  (declare (type fixnum n))
  (let ((res 0))
    (declare (type fixnum res))
    (dotimes (i n)
      (incf res (the fixnum (test-struct-fld1 o)))
      (setf (test-struct-fld2 o) (test-struct-fld1 o)
            (test-struct-fld3 o) (test-struct-fld2 o)
            (test-struct-fld4 o) (test-struct-fld3 o)
            (test-struct-fld5 o) (test-struct-fld4 o)
            (test-struct-fld1 o) (- res 1)
            o (test-struct-next o)
            res (mod res 16)))
    res))

(defun main ()
  (let* ((o1 (make-test-struct :fld1 1))
         (o2 (make-test-struct :fld1 1 :next o1))
         res)
    (setf (test-struct-next o1) o2)
    (setf res (inner o1 *times*))
    (format t "~S~%~S~%" (test-struct-fld5 o1) res)))

(let ((*trace-output* *standard-output*))
  (time (main)))
;;;; EOF
;; запускаем
>(load (compile-file "~/py/struct-benchmark.lisp"))
  0.394 seconds of real time
  0.436000 seconds of total run time (0.436000 user, 0.000000 system)
Лисп, но вместо inline ставим notinline - все аксессоры превращаются в полноценные функции. Получаем
real time 3.879 seconds
Лисп на CLOS:
;; clos-benchmark-with-types.lisp
(in-package :cl-user)

(list
 '#.(restrict-compiler-policy 'safety)
 '#.(restrict-compiler-policy 'debug))
 
(declaim
 (optimize (speed 3) (safety 0) (debug 0)
           (space 0) (compilation-speed 0)))

(defparameter *times* (* 100 1000 1000))

(defclass test-class-3 ()
  ((fld1 :initarg :fld1 :accessor test-class-3-fld1)
   (fld2 :accessor test-class-3-fld2)
   (fld3 :accessor test-class-3-fld3)
   (fld4 :accessor test-class-3-fld4)
   (fld5 :accessor test-class-3-fld5)
   (next :initarg :next :accessor test-class-3-next)))

(defun inner (o n)
  (declare (type fixnum n))
  (declare (type test-class-3 o))
  (let ((res 0))
    (declare (type fixnum res))
    (dotimes (i n)
      (incf res (the fixnum (test-class-3-fld1 o)))
      (setf (test-class-3-fld2 o) (the fixnum (test-class-3-fld1 o))
            (test-class-3-fld3 o) (the fixnum (test-class-3-fld2 o))
            (test-class-3-fld4 o) (the fixnum (test-class-3-fld3 o))
            (test-class-3-fld5 o) (the fixnum (test-class-3-fld4 o))
            (test-class-3-fld1 o) (the fixnum (- res 1))
            o (test-class-3-next o)
            res (mod res 16)))
    (print res)
    res))

(defun main()
  (let* (
         (o1 (make-instance 'test-class-3 :fld1 1))
         (o2 (make-instance 'test-class-3 :fld1 1 :next o1))
         res)
    (setf (test-class-3-next o1) o2)
    (setf res (inner o1 *times*))
    (format t "~S~%~S~%" (test-class-3-fld5 o1) res)))

(let ((*trace-output* *standard-output*))
  (time (main)))
#|
6.115секунд
|#

python:

# ~/py/oop-benchmark.py
import time

__times__ = 100*1000*1000

class TestClass3(object):
  
    def __init__(self):
        self.fld1 = 1
        self.fld2 = 0
        self.fld3 = 0
        self.fld4 = 0
        self.fld5 = 0
        self.next = self

def inner(o,count):
    res = 0
    for i in xrange(count):
        res += o.fld1
        o.fld2 = o.fld1
        o.fld3 = o.fld2
        o.fld4 = o.fld3
        o.fld5 = o.fld4
        o.fld1 = res - 1
        o = o.next;
        res = res % 16
    return res

def my_main():
    o1 = TestClass3()
    o2 = TestClass3()
    o1.next = o2
    o2.next = o1
    res = inner(o1,__times__)
    print '%s' % o1.fld5
    print '%s' % res

my_main()

# запуск:
#time python oop-benchmark.py 
#266666656
#3
#real	0m51.031s
#user	0m50.696s
#sys	0m0.052s
FreePascal
{oop-benchmark.fpc}
{$mode ObjFPC}
const n=100*1000*1000;
type
  PTest = ^TTest;
  TTest = object
  public
    fld1, fld2, fld3, fld4, fld5: Integer;
    next: PTest;
  end;

function inner(o: PTest; bound: Integer): Integer;
var i: Integer;
begin
  Result:=0;
  for i:=0 to bound-1 do with o^ do begin
    Result:=Result+fld1;
    fld2:=fld1;
    fld3:=fld2;
    fld4:=fld3;
    fld5:=fld4;
    fld1:=Result-1;
    o:=next;
    Result:=Result mod 16;
  end;
end;

var
  o1, o2: TTest;
  b: Integer;
begin
  o1.fld1:=1; o1.next:=@o2;
  o2.fld1:=1; o2.next:=@o1;
  b:=inner(@o1,n);
  WriteLn(o1.fld5,' ',b);
end.
{

fpc -O3 oop-benchmark.fpc
Free Pascal Compiler version 2.6.4+dfsg-4 [2014/10/14] for x86_64
Copyright (c) 1993-2014 by Florian Klaempfl and others
Target OS: Linux for x86-64
Compiling oop-benchmark.fpc
Linking oop-benchmark
/usr/bin/ld.bfd: warning: link.res contains output sections; did you forget -T?
36 lines compiled, 0.1 sec 
1 warning(s) issued
den73@deb8:~/py$ time ./oop-benchmark
266666656 3

real	0m1.810s
user	0m1.776s
sys	0m0.008s
}

cython, pypy

##########
# test.pyx file

import cython

cdef class TestClass:

    cdef public unsigned int fld1, fld2, fld3, fld4, fld5
    cdef public object next
    
    def __cinit__(self):
        self.fld1 = 1
        self.fld2 = 0
        self.fld3 = 0
        self.fld4 = 0
        self.fld5 = 0
        self.next = self

@cython.overflowcheck(False)
@cython.cdivision(True)
cdef inner(TestClass o, count):
    cdef unsigned int res = 0, i
    for i in range(count):
        res += o.fld1
        o.fld2 = o.fld1
        o.fld3 = o.fld2
        o.fld4 = o.fld3
        o.fld5 = o.fld4
        o.fld1 = res - 1
        o = o.next;
        res = res % 16
    return res

def main():
    cdef TestClass o1, o2
    o1 = TestClass()
    o2 = TestClass()
    o1.next = o2
    o2.next = o1
    res = inner(o1, 100_000_000)
    print('%s' % o1.fld5)
    print('%s' % res)

##########
# setup.py file

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

# to compile a module:
# python setup.py build_ext --inplace

extensions = [
    Extension('test', ['test.pyx'],
              extra_compile_args=['-O3'])
]

setup(name = 'access attrs benchmark',
      ext_modules = cythonize(extensions, annotate=True),
)

#########
# main.py file

from test import *
main()

# Запуск с помощью pip 
# 1. Создать ТРИ файла, исходник которых дан выше
# 2. В этой директории:
python setup.py build_ext --inplace
time python main.py

# Результаты:
real	0m0.306s
user	0m0.304s
sys	0m0.004s

$ time pypy oop-benchmark.py 
266666656
3

real	0m0,761s
user	0m0,736s
sys	0m0,020s

EMACS Lisp - несколько быстрее Питона.

★★★★★

Последнее исправление: den73 (всего исправлений: 17)

Ответ на: комментарий от Virtuos86

Зетестил на своей микроволновке:

root# cat ./oop_benchmark.py
TIMES = 100*1000*1000

class TestClass(object):
    __slots__ = 'fld1', 'fld2', 'fld3', 'fld4', 'fld5', 'next'
    def __init__(self):
        self.fld1 = 1
        self.fld2 = 2
        self.fld3 = 3
        self.fld4 = 4
        self.fld5 = 5
        self.next = self
        
def inner(obj, count):
    res = 0
    for i in xrange(count):
        res += obj.fld1
        obj.fld2 = obj.fld1
        obj.fld3 = obj.fld2
        obj.fld4 = obj.fld3
        obj.fld5 = obj.fld4
        obj.fld1 = res - 1
        obj = obj.next
        res = res % 16
    return res

def main():
    obj1 = TestClass()
    obj2 = TestClass()
    obj1.next = obj2
    obj2.next = obj1
    res = inner(obj1, TIMES)
    print obj1.fld5
    print res

if __name__ == '__main__':
    main()
root# time python ./oop_benchmark.py 
266666656
3

real	1m26.563s
user	1m26.550s
sys	0m0.010s
root# cat ./oop_benchmark.py
TIMES = 100*1000*100

class TestClass(object):
    def __init__(self):
        self.fld1 = 1
        self.fld2 = 2
        self.fld3 = 3
        self.fld4 = 4
        self.fld5 = 5
        self.next = self
        
def inner(obj, count):
    res = 0
    for i in xrange(count):
        res += obj.fld1
        obj.fld2 = obj.fld1
        obj.fld3 = obj.fld2
        obj.fld4 = obj.fld3
        obj.fld5 = obj.fld4
        obj.fld1 = res -1
        obj = obj.next
        res = res % 16
    return res

def main():
    obj1 = TestClass()
    obj2 = TestClass()
    obj1.next = obj2
    obj2.next = obj1
    res = inner(obj1, TIMES)
    print obj1.fld5
    print res

if __name__ == '__main__':
    main()
root# time python ./oop_benchmark.py 
266666656
3

real	1m53.365s
user	1m53.355s
sys	0m0.008s
root#

Virtuos86 ★★★★★
()
Ответ на: комментарий от KRoN73

Вторая часть решается так: sudo apt-get install sbcl

Ты вручную парсишь? Я хочу запускать не вызовом time из ОС (т.к. рантайм тяжёлый), а вызовом лисповой time, к-рая что-то печатает. Т.е. stdout будет содержать ещё что-то, кроме собственно цифры времени.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Ты вручную парсишь?

Время? Да. Оценка инициализации рантайма вопрос, в общем, неоднозначный. В скриптовых/прикладных задачах это важный критерий, на сервере — мало важный.

Т.е. stdout будет содержать ещё что-то, кроме собственно цифры времени.

Пофиг. В некоторых тестах так и сделано :)

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

Ну ты просто озвучь свои правила, а я дальше подумаю. Потому что если ты включаешь время старта рантайма, то лисп явно сольёт. Как минимум нужно собирать образ и выкидывать из него максимум возможного. У меня сейчас нет ресурсов на такие упражнения :)

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

А вообще, к слову, у тебя плохие тесты. Динамические языки, хранящие поля в словарях, работают тем медленнее, чем больше в объекте полей. То же касается и С++, к-рая умеет кешировать поля объекта в регистрах (поэтому мой тест сначала вообще выдавал фигню). Возможно, случай С++ для тебя неакутален, а вот полей надо было бы побольше. Учти, когда будешь делать следующую версию.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Погоди, так ты в С++ тесте замерял скорость помещения адрес в регистр? Это вообще лол. Оно, что кэширует, что не кэширует - разница в считанных тактах.

anonymous
()
Ответ на: комментарий от den73

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

Что это за тесты и что ими меряют? Вопрос скорости при доступе к памяти, это вопрос кеша. При обращении подтягивается не только элемент, к которому идет обращение, а cach line по размеру кеша процессора. Если доступ происходит к следующим элементам, которые уже в кеше — скорость одна, если нет в кеше — другая. Процессор пытается предсказать твои обращения к памяти, как это делается, зависит от политики prefetch. При доступе к случайной ячейке памяти надо заново тянуть cache line. К тому же будет куча зависимостей от размера кешей L1, L2, L3.

iVS ★★★★★
()
Ответ на: комментарий от den73

То же касается и С++, к-рая умеет кешировать поля объекта в регистрах

Вот только когда высчитывается fib(40), то никаких регистров не хватит :D А вот с выделением памяти — возможны варианты. Именно поэтому С++ там в вариантах Stack и Heap (ну и Boost до кучи).

KRoN73 ★★★★★
()
Ответ на: комментарий от den73

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

Да нет, можно без рантайма считать :)

KRoN73 ★★★★★
()
Ответ на: комментарий от iVS

Я в теме несколько раз ответил.

den73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

Ну я и говорю, что скорее всего это не имеет места. Я уж не знаю, как пишут объекты нормальные люди, но у меня зачастую полей не меньше 10 (бывает и 50), и то же я вижу в других тулкитах. Если какой-то скриптовый язык хранит поля в массивах с перебором, к-рые при достаточном росте превращаются в хеш-таблицу, то объект с двумя полями не даёт правильного представления о производительности данного языка на реальных задачах. Как устроены ООП системы в Питоне и Лиспе внутри, я знаю недостаточно хорошо. Но точно то, что лучше делать пример, в котором всё более-менее реалистично.

Понятно, что это трудоёмко, так что это я так, бурчу.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

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

А сколько полей там не будет, язык со статической типизацией это ещё на этапе компиляции разрулит. 2 свойства или 200, у Си++ это превратится в одинаковый код.

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

А меня смущает время жизни объектов, они не передаются за пределы метода, например jvm после эскейп анализа их 100% аллоцирует в стеке. В реальных приложениях объекты некоторое время живут.

Aber ★★★★★
()

Оказывается, тема-то не исчерпана. Думаю, все полтора присутствующие здесь лиспера уже плюнули мне вслед и заигнорили, а я вот что нашёл:

https://github.com/guicho271828/inlined-generic-function

За счёт инлайнинга родовых функций ускоряется в 10 раз. Правда, это всё равно не покрывает разницы в скорости между структурами и CLOS, но покрывает её большую часть.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Тест работы с объектами должен включать в себя передачу объекта в метод и возвращение объекта из метода. Еще я думаю, что стоит в объектных и процедурно-алгоритмических языках рекурсию заменить на циклы. Потому как в них нет tail call optimization, зато перестают работать все прочие оптимизации типа inline, легко поймать stack overflow, да и нет культуры писать рекурсивные алгоритмы за пределами функциональных языков и lisp диалектов.

Aber ★★★★★
()

Почему для тестов не берется задача приближенная к реальным программам? Языки-то для чего вообще? Писать алгоритмы, программы.

Например задача: размытие по гаусу Lena test photo.

И будет ясно что надо встроить в gimp, чтоб не тормозил на этом эффекте.

Польза же?

ult
()
Ответ на: комментарий от ult

Нужен тест чисто на объекты, а если размытие, там ещё наверняка математика. С математикой могут быть отдельные трудности. Например, в лиспе нет 64-разрядных целых, там есть только 62 разрядные (или 61, точно не помню).

den73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

Кстати, язык типа С++ в принцие способен этот факториал целиком посчитать на этапе компиляции, если 40 задано константой.

den73 ★★★★★
() автор топика
Ответ на: комментарий от Oxdeadbeef

Это отдельный тип, и он отличается от обычных 64 разрядных целых - у него должен быть таг типа, иначе я не знаю, как он со сборкой мусора работает. Т.е. это не просто 64 разрядное целое, а как бы запись из одного поля. Не исключено, что это дороже, хотя может быть и нет. Без опыта не узнаешь.

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от ult

В принципе, конечно, можно взять что-то из Rosetta Code, но опять же - придётся оттуда обосабливать именно работу собъектами. И кстати, двоичные деревья тоже не подойдут, у них тоже мало полей.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Там написано что оптимизатор сделает как надо, будет работать как с RAW 64 битами. Но только работает с 64 битным LispWorks. Надо будет скомпилить и посмотреть на ассемблер.

Oxdeadbeef ★★★
()
Ответ на: комментарий от Oxdeadbeef

Подозреваю, что если такое целое вернуть из top-level-функции, то будет боксинг. Или там просто меньше значимых битов

dave ★★★★★
()
Последнее исправление: dave (всего исправлений: 1)
Ответ на: комментарий от den73

А значит, хороший тест должен считать не фибоначчи, а что-то более сложное.

Есть почти такая же фигня — рекурсивная функция Аккермана :) Но там вообще жесть. Либо считается мгновенно и быстро, либо сразу начинается такой уровень, что у массы стандартных приложений не хватает дефолтовой глубины стека. Помню, под Windows (я ещё как числодробилку использовал) приходилось exe-шники дотнета ручками патчить на предмет размера стека :D

KRoN73 ★★★★★
()
Ответ на: комментарий от Aber

А меня смущает время жизни объектов

Оно не должно смущать :) Потому что метод рекурсивный. И объекты не освободятся, пока не обсчитаются вложенные объекты. А они не освободятся, пока не обсчитаются их вложенные объекты... И цепочка очень длинная и разветвлённая. Память в таком, объектом виде жрётся очень активно.

KRoN73 ★★★★★
()
Ответ на: комментарий от Aber

Тест работы с объектами должен включать в себя передачу объекта в метод и возвращение объекта из метода

Хм. Можно подумать. Сомневаюсь, что это сильно повлияет на расклад, но попробовать стоит.

Еще я думаю, что стоит в объектных и процедурно-алгоритмических языках рекурсию заменить на циклы.

Тогда, как раз, задача сильно упростится. А у нас стоит задача не посчитать Фибоначчи, для этого есть хвостовая рекурсия (циклы тоже, да) и гамма-функция. У нас задача оценить работу с объектным кодом — создание/освобождение объектов, вызовы методов...

KRoN73 ★★★★★
()
Ответ на: комментарий от den73

Кстати, язык типа С++ в принцие способен этот факториал целиком посчитать на этапе компиляции, если 40 задано константой

Именно так. И не он один :) Поэтому задача и не стоит посчитать. Задача стоит потрах^W тестировать работу с объектами :)

Более того, у C++11 пришлось вводит аргумент с командной строки. Потому что даже в таком объектом виде без всяких ухищрений компилятор из исходной константы порождал константный же код :)

KRoN73 ★★★★★
()
Ответ на: комментарий от KRoN73

что это сильно повлияет на расклад

Сильно повлияет на jvm, объекты выйдут за скоп метода, а это значит, что их аллокация произойдет в куче а не в стеке. Не знаю как повлияет это на скорость, но начнет работать gc. В cpp11 может сработать return value optimization для объекта возвращаемого из метода, я правда не знаю как там с рекурсией.

Aber ★★★★★
()
Ответ на: комментарий от Virtuos86

они и не могут. никто не утверждал обратного. макаки вообще ничего не могут. поэтому они и пистоны не нужны.

Iron_Bug ★★★★★
()
Ответ на: комментарий от anonymous

В общем случае да, попутал я. Это в C# и Java - такое. Но среди C++ - в MSVC++

lovesan ★★
()

Python для объектов использует hash table. Уже после одного этого сравнивать в этом тесте с C++ это логическая ОШИБКА. Для классов на основе C struct есть компилятор Cython(реализует большой спектр между Python и C).

макаки вообще ничего не могут. поэтому они и пистоны не нужны.

Макаки это наверное биологи(биохимики), астрономы, математики, аналитики данных, и т.д. Они давно используют Python в своей работе(кроме РФ и постсовка где наука это только наследие совка в виде архивных чертежей военной техники и ракет).

Из всех этих языков нормальный терминал(command shell) есть только у Python(IPython). В Common Lisp REPL в виде компиляции на лету выделенных участков кода с добавлением в образ работающей программы в памяти, что приятно в SLIME.

На моём компе(«машина») код на Python проходит примерно за такое же время как в OP: 51~52 секунды. Можно убрать 10 секунд просто добавлением в TestClass этой строки:

__slots__ = ('fld1', 'fld2', 'fld3', 'fld4', 'fld5', 'next')

Теперь это класс не хранящий аттрибуты в hash table, в итоге 41 секунда. Числительные типы в Python это объекты а не набор битов, потому никто не ожидает скорости вычислений изначально, для этого есть C.

__times__ = 100_000_000

class TestClass:

    __slots__ = ('fld1', 'fld2', 'fld3', 'fld4', 'fld5', 'next')
    
    def __init__(self):
        self.fld1 = 1
        self.fld2 = 0
        self.fld3 = 0
        self.fld4 = 0
        self.fld5 = 0
        self.next = self

def inner(o, count):
    res = 0
    for i in range(count):
        res += o.fld1
        o.fld2 = o.fld1
        o.fld3 = o.fld2
        o.fld4 = o.fld3
        o.fld5 = o.fld4
        o.fld1 = res - 1
        o = o.next;
        res = res % 16
    return res

def my_main():
    o1 = TestClass()
    o2 = TestClass()
    o1.next = o2
    o2.next = o1
    res = inner(o1, __times__)
    print('%s' % o1.fld5)
    print('%s' % res)

my_main()
tp_for_my_bunghole
()
Ответ на: комментарий от tp_for_my_bunghole

Макаки это наверное биологи(биохимики), астрономы, математики, аналитики данных, и т.д. Они давно используют Python в своей работе

Вот так просто можно перестать быть макакой. Идите в астраномы и ваш код станет мягким и шелковистым.

anonymous
()

Результаты тестов в original post;

C++: 0.225s
Common Lisp: 0.394 seconds (полная оптимизация)
Common Lisp CLOS: 6.115 секунд
Python: 51.031s
Free Pascal: 1.810s

После оптимизации Python через __slots__ выше: 41 seconds, на 20% быстрее.

Ниже вариант в Cython, который генерирует модуль для Python: 0.285s;
Т.е. это уже на одном уровне с C++. В Emacs подсветка кода для ".pyx"(cython) конечно лучше.

##########
# test.pyx file

import cython

cdef class TestClass:

    cdef public unsigned int fld1, fld2, fld3, fld4, fld5
    cdef public object next
    
    def __cinit__(self):
        self.fld1 = 1
        self.fld2 = 0
        self.fld3 = 0
        self.fld4 = 0
        self.fld5 = 0
        self.next = self

@cython.overflowcheck(False)
@cython.cdivision(True)
cdef inner(TestClass o, count):
    cdef unsigned int res = 0, i
    for i in range(count):
        res += o.fld1
        o.fld2 = o.fld1
        o.fld3 = o.fld2
        o.fld4 = o.fld3
        o.fld5 = o.fld4
        o.fld1 = res - 1
        o = o.next;
        res = res % 16
    return res

def main():
    cdef TestClass o1, o2
    o1 = TestClass()
    o2 = TestClass()
    o1.next = o2
    o2.next = o1
    res = inner(o1, 100_000_000)
    print('%s' % o1.fld5)
    print('%s' % res)

##########
# setup.py file

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

# to compile a module:
# python setup.py build_ext --inplace

extensions = [
    Extension('test', ['test.pyx'],
              extra_compile_args=['-O3'])
]

setup(name = 'access attrs benchmark',
      ext_modules = cythonize(extensions, annotate=True),
)

#########
# main.py file

from test import *
main()

tp_for_my_bunghole
()
Ответ на: комментарий от tp_for_my_bunghole

Т.е. это уже на одном уровне с C++

Питон и так на Си написан. Дело не в языке, а в 100500 абстракциях, необходимых для простейших вычислений. Это как идти за хлебом через пересадку в Стамбуле, вместо того, чтобы просто сходить в магаз возле дома.

anonymous
()
Ответ на: комментарий от anonymous

Питон и так на Си написан. Дело не в языке, а в 100500 абстракциях, необходимых для простейших вычислений.

На C? Вы всё врёти. Я не встречал утверждений о том что Python предназначался для «простейших вычислений». Но макаки об этом наверное не знают.

Как протестировать код выше:

Создать папку, например 'test'. В ней вызвать:

python3 -m venv my_env

Появится папка my_env содержащая новую среду Python(вместо глобально установленной через пакетный менеджер). Создать 3 файла данные выше. Активировать эту среду в терминале, установить в ней компилятор Cython, собрать модуль и протестировать:

source ./my_env/bin/activate
pip install cython
python setup.py build_ext --inplace
time python main.py

Результат:

C++: 0.225s
Python/Cython: 0.285s

Из этих 0.285s, запуск интерпретатора Python занимает 0.022s;

tp_for_my_bunghole
()
Последнее исправление: tp_for_my_bunghole (всего исправлений: 1)
Ответ на: комментарий от tp_for_my_bunghole

IPython это лучшая командная оболочка вообще среди всех языков программирования.
Чем привлекателен Python на примере?

Есть action camera, с углом обзора 170 градусов, сильный fish eye(рыбий глаз) эффект. Его можно исправить в изображении передвигая каждый пиксель дальше от центра по радиусу, корректируя радиус согласно какой-то функции(график), по углам картинка получится сильно растянутой, но с правильными линиями и пропорциями.
Как узнать функцию этой линзы от центра до угла картинки?
В Python это можно определить за очень короткое время.

Нужно создать list с ~5-ю числами в интервале 0.0-1.0, приблизительные точки на графике, коэффициенты увеличения радиуса;
Библиотека numpy подсчитает интерполяцию всех остальных точек графика, по заданому количеству точек. Есть график, его можно сразу же красиво отрендерить в окне через mathplotlib одной строкой. Есть массив этих точек, в библиотеке для изображений как PIL проверяешь эту функцию изменяя радиус для каждого пиксела. Изменение любой из изначальных 5-и точек изменяет график, так можно определить функцию широкоугольной линзы чтобы исправлять изображение(все прямые линии должны быть на картинке прямыми.

Потом уже думаешь об оптимизации алгоритмов, библиотек. Примерно так работают и учёные, scipy, numpy, jupyter. Или просто профессионалы создающие прототип.

tp_for_my_bunghole
()
Ответ на: комментарий от tp_for_my_bunghole

Я не встречал утверждений о том что Python предназначался для «простейших вычислений».

Питон предназначался для простейших. А сложность вычислений может быть любой, на производительности питона это заметно не отразится =)

anonymous
()
Ответ на: комментарий от anonymous

Python задумывался как язык управления для ОС нового типа, в отличии от простой оболочки как в UNIX. В конце 80-х, начале 90-х.

Для простейших задумывался язык Java, по заказу для корпоративной среды(одним из создателей Emacs - James Gosling).

tp_for_my_bunghole
()
Последнее исправление: tp_for_my_bunghole (всего исправлений: 2)
Ответ на: комментарий от tp_for_my_bunghole

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

А про жабу это ты хорошо сказал, тут и не поспоришь =)

anonymous
()
Ответ на: комментарий от anonymous

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

Нет, не для UNIX, и не как оболочку управления процессами. ОС предполагались с управлением более низкоуровневыми элементами, совсем другая парадигма. Изначально Python разрабатывался как полнофункциональный язык программирования с сильной типизацией.

В Python и Common Lisp можно получить тип любого объекта, классы создают тип. В стандарте Scheme этого нет. Про другие говорить нечего, во Free Pascal скопировали RTTI из Delphi, неуклюже но наверное лучше чем в что там в C++.

В Python работа с типами лучше развита чем в CL. Развивается статическая проверка типов. Типы в синтаксисе Python 3, но без семантики - она оставлена для инструментов как MyPy.

Одна из проблем Python это адаптация структуры API для потоков(threads) из Java на раннем этапе. От этого стали зависеть, и оно устоялось.
Также Python предоставляет публичный C API, это накладывает ограничения по изменению реализации но открывает возможности для создания нативных модулей.
PYPY решили не предоставлять C API чтобы работать как с движущейся целью, у такого подхода тоже свои недостатки.

tp_for_my_bunghole
()
Последнее исправление: tp_for_my_bunghole (всего исправлений: 1)
Ответ на: комментарий от tp_for_my_bunghole

Макаки это наверное биологи(биохимики), астрономы, математики, аналитики данных, и т.д. Они давно используют Python в своей работе

То, что ученые и метематики обладают некими сверхсекретными знаниями, недоступными простым смертным — всего лишь голливудский миф. На деле же они обычные дурачки-среднячки, винтики коррумпированной бюрократии с низкой продуктивностью. И Python как раз их уровень.

anonymous
()
Ответ на: комментарий от tp_for_my_bunghole

Не могу сказать за анонимуса выше, но то, что питон позиционируется как язык для непрограммистов, говорит не только об уровне тех, кто на нем пишет, но и о качестве среднестатистического кода на этом языке. Так какая причина нормальным людям, осилившим нормальные программистские языки, структуры, алгоритмы, etc, любить питон? Пусть его любят астрономы и биологи, им это простительно.

anonymous
()
Ответ на: комментарий от anonymous

астрономы и биологи, им это простительно

Тогда для программиста непростительно балаболить, не приводя конкретные аргументы по Python и примеры «нормальных языков».
«Вот это вот всё», алгоритмы, структуры.

tp_for_my_bunghole
()
Ответ на: комментарий от tp_for_my_bunghole

не приводя конкретные аргументы по Python

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

примеры «нормальных языков»

Нормальный для чего? Системщина - Си. Общее назначение - С++. Веб - PHP, js.

«Вот это вот всё», алгоритмы, структуры.

Да, есть такие вещи, но всякого рода питонерам их знать не обязательно, за них это делают настоящие программисты.

anonymous
()
Ответ на: комментарий от anonymous

«Вот это вот всё», алгоритмы, структуры.

Да, есть такие вещи, но всякого рода питонерам их знать не обязательно

Было предложено аргументировать конкретно в контексте алгоритмов и структур, но анонимус не знает ни Python, ни структур судя по всему.
«Настоящий программист».

Веб - PHP, js

А, вот они, те загадочные нормальные языки в отличии от Python, которые умалчивались на предыдущей странице.
«Посвяти свою жизнь программированию». Тема исчерпана.

tp_for_my_bunghole
()
Последнее исправление: tp_for_my_bunghole (всего исправлений: 1)
Ответ на: комментарий от anonymous

за них это делают настоящие программисты

Пример из жизни по использованию Python как «секретного оружия» в патентном судебном разбирательстве с позиции нанятого программиста:
David Beazley: Discovering Python - PyCon 2014

В завершении работы он должен был участвовать в 8-часовом заседании отвечая на вопросы стороны обвинения. То что он рассказывает не пришло бы в голову любому другому «нормальному программисту», за эту «ненормальность» ему наверное хорошо и заплатила компания.

Пo структурам данным можно посмотреть на numpy, многоуровневые массивы, их использует например OpenCV для внутреннего представления пикселей изображений и параллельных плоскостей ассоциированных с пикселами.

tp_for_my_bunghole
()
Последнее исправление: tp_for_my_bunghole (всего исправлений: 1)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.