LINUX.ORG.RU
ФорумTalks

Код-инжекшены в убогом питон

 , ,


1

4

да выбрасывает ексепшен, но выброс ексепшена == борьба со следствиями, а именно с кодинжекшеном.

>>> class Foo():
...     def __init__(self, **kw):
...             print(kw)
... 
>>> d = {'self': 'abc', 'a': 1, 'b': 2}
>>> 
>>> Foo(**d)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: __init__() got multiple values for argument 'self'

Ща адепты мне расскажут что так делать нельзя.

Типа в шаблон jinja нельзя передавать self. Придумайте новое имя. Ага.

Для специалистов: если атаковать не первый параметр (self), а скажем опциональный, то будет весело:

>>> class Foo():
...     def __init__(self, a=None, **kw):
...             print('a=', a, kw)
... 
>>> d = {'self1': 'abc', 'a': 1, 'b': 2}
>>> 
>>> Foo(**d)
a= 1 {'self1': 'abc', 'b': 2}
<__main__.Foo object at 0x7f9a14ec5a58>
>>>
★★

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

Будете делать numba — не забудьте прогреть, а то мерять будете производительность JIT-компилятора вместо исполнения кода.

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

Другие я тоже не грел. Это те издержки с которыми надо считаться. Достаточно и того, что тест делает скидку на время запуска, а не просто time fib.py n.

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

Другие я тоже не грел. Это те издержки с которыми надо считаться.

Разупорись. Время компиляции кода на Cython ты в измерения не включал.

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

Разупорись. Время компиляции кода на Cython ты в измерения не включал.

Для jit-ов это не актуально. Там это время — доли секунды:

20 numbers: 0.009889 -> PyPy3

У нумбы оно побольше, но всё равно теряется на фоне общего времени. У меня на медленной машине:

$ time python fib_numba.py 1
1

real	0,764s
user	0,576s
sys	0,208s

$ time python fib_numba.py 45
1134903170

real	31,757s
user	31,100s
sys	0,280s

Да и вообще я слабо себе представляю, что такое «прогреть» в данном случае, функция же итак вызывается миллионы раз... Например, такой код считается прогретым?

import sys, numba

@numba.jit
def fib(n):
    if n < 2:
        return n
    return fib(n-2) + fib(n-1)

if __name__ == "__main__":
    print(fib(int(sys.argv[1])))

Но ок, я поддерживаю, WitcherGeralt, если можно, для сравнения выложи для нумбы время работы для аргумента 1 и 10.

pynonymous
()

Тестил с cpupower frequency-set -g performance, всё собрано с -march=native -O3.

Кроме запуска с tcc, там просто tcc -run, но он в любом случае сливает и присутствует в тесте просто как аналог go run, с которым запущен тест на Go.

Плюс ещё один тест с -Ofast (хз, что это, в доке этого не вижу), как посоветовал @andalevor.

@pynonymous @t184256 @pawnhearts

42 numbers: 1.224374 -> C (gcc)
42 numbers: 1.223540 -> C (gcc -Ofast)
42 numbers: 3.740965 -> C (tcc)
42 numbers: 2.005909 -> C (clang)
42 numbers: 1.221111 -> Cython
42 numbers: 2.435253 -> Go
42 numbers: 1.973891 -> Go (gccgo)

-> Java
42 numbers: 1.475000
42 numbers: 1.472000
42 numbers: 1.472000

-> JavaScript
42 numbers: 4.15
42 numbers: 4.139
42 numbers: 4.138

-> Lua (luajit)
42 numbers: 2.969766
42 numbers: 2.974916
42 numbers: 2.967843

-> PyPy
42 numbers: 11.395654
42 numbers: 11.378227
42 numbers: 11.377777

-> numba
42 numbers: 2.890487
42 numbers: 2.862221
42 numbers: 2.855045
WitcherGeralt ★★
()
Ответ на: комментарий от WitcherGeralt

Cython

+06: cdef int fib(int n):
static int __pyx_f_5cyfib_fib(int __pyx_v_n) {
  int __pyx_r;
  __Pyx_RefNannyDeclarations
  __Pyx_RefNannySetupContext("fib", 0);
/* … */
  /* function exit code */
  __pyx_L0:;
  __Pyx_RefNannyFinishContext();
  return __pyx_r;
}
+07: 	if n < 2:
  __pyx_t_1 = ((__pyx_v_n < 2) != 0);
  if (__pyx_t_1) {
/* … */
  }
+08: 		return n
    __pyx_r = __pyx_v_n;
    goto __pyx_L0;
+09: 	return fib(n - 1) + fib(n - 2)
  __pyx_r = (__pyx_f_5cyfib_fib((__pyx_v_n - 1)) + __pyx_f_5cyfib_fib((__pyx_v_n - 2)));
  goto __pyx_L0;
WitcherGeralt ★★
()
Ответ на: комментарий от WitcherGeralt

ОК, согласен, на этом бенче производительности вызовов numba сливает.

Я бы замерил что-то более числодробительное, например:

import math
import numpy as np

N = 1_000_000
x = np.random.uniform(-1, +1, N)
y = np.random.uniform(-1, +1, N)
r = np.zeros(N)
%%cython -a -c-O3
cimport libc.math
cimport cython
cdef long N = 1_000_000
@cython.boundscheck(False)
def hypot_cython(double[:] x, double[:] y, double[:] r):
    cdef long i
    for i in range(N):
        r[i] = libc.math.sqrt(x[i]**2 + y[i]**2)
%timeit hypot_cython(x, y, r)
6.78 ms ± 35 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
@numba.jit('void(f8[:], f8[:], f8[:])')
def hypot_numba(x, y, r):
    for i in range(N):
        r[i] = math.sqrt(x[i]**2 + y[i]**2)
hypot_numba(x, y, r)
%timeit hypot_numba(x, y, r)
6.58 ms ± 28.8 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
t184256 ★★★★★
()
Ответ на: комментарий от WitcherGeralt

Благодарю. Отличный тест!

Странно, что gccgo отстаёт от остальных сишных. Не представляю, чем это может быть вызвано.

как аналог go run, с которым запущен тест на Go.

Можно его в бинарник собирать: go build -o fib_bin fib.go но вряд ли это сильно изменит результат.

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

Я бы замерил что-то более числодробительное, например: ...

Чисел бы побольше, а то 6 миллисекунд — маловато. И раз мы используем numpy, наверное, надо подключать cimport numpy as np и делать Efficient indexing... Только вот...

Только вот тест фибоначчи сравнивал, в основном, оверхед на вызов функций. А что мы будем сравнивать тут? Скорость вычисления sqrt? Подозреваю, тогда победит numpy.sqrt с примерно одинаковой скоростью, откуда бы его ни вызвали.

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

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

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

Кстати, на что ещё внимание обратил внимание: Go потребляет 1.4мб оперативы, а собранное gccgo аж 19мб.

К слову, С 740кб, luajit всего 1.2мб, а numba с pypy по 80 и 77, соответственно, cython 7.

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

Чисел бы побольше, а то 6 миллисекунд — маловато.

Я код дал — действуй.

И раз мы используем numpy, наверное, надо подключать cimport numpy as np и делать Efficient indexing…

Нет, не надо, у меня доступ через buffer interface.

Только вот тест фибоначчи сравнивал, в основном, оверхед на вызов функций. А что мы будем сравнивать тут?

Эффективность доступа к памяти, sqrt, арифметику.

Скорость вычисления sqrt? Подозреваю, тогда победит numpy.sqrt с примерно одинаковой скоростью, откуда бы его ни вызвали.

А ты обгони =)

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

Эффективность доступа к памяти, sqrt, арифметику.
А ты обгони =)

Как оказалось — уже: https://pybenchmarks.org/

Тест n-body — это как раз тест арифметики: куча операций с плавающей точкой над элементами массива. Результаты:

58 сек — Numba
44 сек — чистый Python3
12 сек — Cython
 6 сек — PyPy

Причём для cython-а там только расставили типы промежуточных переменных, даже оставили питоновые массивы (поэтому pypy его обходит). И пусть даже они включили для Numba несколько секунд времени компиляции. Но при такой разнице — это уже не существенно.

PS: На моей машине numba вообще падает с Segmentation fault при попытке запустить n-body.

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

Открыл, увидел там питоновские списки (!), закрыл. Я рад и удивлен, что PyPy это смог оптимизировать. Я ни капли не удивлен, что Numba не ускорила доступ к питонообъектам. Numba — она для удобного ускорения того, что собирается с nopython (как впрочем и на Cython хороший код — с nogil). То, что они позволяют прозрачно сношать питонообъекты — приятная побочка, не более, и уж точно не их problem domain.

Что еще будете ускорять средствами для числодробления, парсинг JSON? Бизнес-логику? Скачивание видео с Youtube?

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

В классическом фортране (fortran 77) нет рекурсивных функций, там выкручивались через псевдопроцедуру (EXTERNAL), может быть напишу, чтобы сравнить. Вот пока говнокод на фортран 90:

!filename fib_rec.f90
  program fibonacci
    integer :: n, fib
    n = 42
    write(*,*) fib(n)
  end program fibonacci

  recursive function fib(n) result(fb)
    integer, intent(in) :: n
    integer :: fb
    if ( n .le. 1 ) then
      fb=n
      return
    end if
    fb=fib(n-1) + fib(n-2)
  end function fib

По умолчанию у фортранов передача переменной в подпрограмму по ссылке, а не по значению. В этот код можно добавить ещё и будет передача по значию, но на моём gfortran без оптимизации разницы не даёт, а с -O3 быстрее передача по ссылке.

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

Пытался написать на f95, ничего не вышло:

recursive function fib(n) result(r)
	real :: r
	integer :: n

	if (n < 2) then
		r = n
		return
	end if
	r = fib(n - 1) + fib(n - 2)
	return
end

program fibtest

integer :: i
real :: t, p

!t = CPU_TIME()

do i=0,42
	p = fib(i)
	write (*,*) p
enddo

!write (*,*) 42, "numbers:", (CPU_TIME(t) - t)

end program fibtest

Компилятор настаивал на возвращении из функции real, не понял что с этим делать. Принять параметр на вход, сколько чисел обходить и запринтить время не вышло и подавно.

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

Компилятор настаивал на возвращении из функции real

В программе (после program fibtest) должно быть:

integer :: fib
иначе тип возвращаемого значения функции (как и типы переменных) определяются по первой букве.

Принять параметр на вход

Если число со stdin, то

read(*,*) n
do i=0,n
если хочется как аргумент командной строки, то там до 2003 единого стандарта не было, но можно для gnu компилятора что-то написать.

запринтить время не вышло и подавно.

cpu_time() это процедура (подпрограмма), а не функция, она ничего не возвращает, изменяет значение переданной её переменной:

call cpu_time(tst) ! в tst будет время в float
...
call cpu_time(tfn)
write(*,*) tfn-tst

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

cpu_time() это процедура (подпрограмма), а не функция

Это я прочёл в документации, но до меня не дошло что с ней делать. Я сначала вставил имя переменной в аргументы, но без call. В итоге просто записал как псевдокод в комментарии.

Спасибо! Теперь тест полный.

42 numbers: 1.55332792 -> Fortran (gfortran)
WitcherGeralt ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.