Python Runtime sem GIL

em Golang: Grumpy

Alan Justino da Silva

alan.justino@yahoo.com.br

@alanjds

PYTHONBRASIL 2017

apoio:

+300 milhões de reais/ano

+2 milhões de pedidos/ano

+250 mil profissionais

+3 mil cidades atendidas

  • +100 pessoas
  • +60 técnic@s
    • ​Principalmente Ruby
    • Python & R
    • Javascript
    • Java & Swift/Objective-C

PARALELISMO CPYTHON

PARALELISMO GOLANG

GRUMPY TRANSPILER

GRUMPY INTERNALS

PARALELISMO CPYTHON

PARALELISMO GOLANG

GRUMPY TRANSPILER

GRUMPY INTERNALS

PARELELISMO CPYTHON

Concorrência != Paralelismo

Paralelismo

Concorrência

Global Interpreter Lock

Green Threads

AsyncIO, Gevent, Twisted, etc...

PARALELISMO CPYTHON

PARALELISMO GOLANG

GRUMPY TRANSPILER

GRUMPY INTERNALS

Não tem

Global Interpreter Lock

PARALELISMO CPYTHON

PARALELISMO GOLANG

GRUMPY TRANSPILER

GRUMPY INTERNALS

Por que Python?

P: Que problema Python tenta resolver?

My observation at the time was that computers were getting faster and cheaper at an incredible rate (...). [But] the cost of the programmers to program them remained not going down. (Guido van Rossum, 2008)

Por que Python?

[How] about the indentation in Python?

It's the right thing to do from a code readability (...) [and] from a maintenance point of view. And maintainability of code is what counts most: no program is perfect from the start, and if it is successful, it will be extended. So maintenance is a fact of life, not a necessary evil. (GvR, 2008)

  • Facil escrita de código (baixa curva de aprendizagem)
  • Fácil leitura e modificação de código (manutenção)

Grumpy Transpiler

Benchmark

def fib(n):
    if n < 2:
        return 1
    return fib(n - 1) + fib(n - 2)

PARALELISMO CPYTHON

PARALELISMO GOLANG

GRUMPY TRANSPILER

GRUMPY INTERNALS

GRUMPY INTERNALS

  • Exemplo
  • Reuso de código (stdlib)
  • Módulos internos

Grumpy Runtime

echo "print 'hello, world'" | make run
echo "print 'hello, world'" | python
package __main__
import πg "grumpy"
var Code *πg.Code
func init() {
	Code = πg.NewCode("<module>", "ehlo.py", nil, 0, func(πF *πg.Frame, _ []*πg.Object) (*πg.Object, *πg.BaseException) {
		var πR *πg.Object; _ = πR
		var πE *πg.BaseException; _ = πE
		var πTemp001 []*πg.Object
		_ = πTemp001
		for ; πF.State() >= 0; πF.PopCheckpoint() {
			switch πF.State() {
			case 0:
			default: panic("unexpected function state")
			}
			// line 1: print 'hello, world'
			πF.SetLineno(1)
			πTemp001 = make([]*πg.Object, 1)
			πTemp001[0] = πg.NewStr("hello, world").ToObject()
			if πE = πg.Print(πF, πTemp001, true); πE != nil {
				continue
			}
		}
		return nil, πE
	})
	πg.RegisterModule("__main__", Code)
}

Grumpy Transpiler

a = 'hello'
b = 'world'
message = ', '.join([a, b])
print message

Grumpy Transpiler

package __main__
import πg "grumpy"
var Code *πg.Code
func init() {
	Code = πg.NewCode("<module>", "ehlo.py", nil, 0, func(πF *πg.Frame, _ []*πg.Object) (*πg.Object, *πg.BaseException) {
		var πR *πg.Object; _ = πR
		var πE *πg.BaseException; _ = πE
		ßa := πg.InternStr("a")
		ßb := πg.InternStr("b")
		ßhello := πg.InternStr("hello")
		ßjoin := πg.InternStr("join")
		ßmessage := πg.InternStr("message")
		ßworld := πg.InternStr("world")
		var πTemp001 []*πg.Object
		_ = πTemp001
		var πTemp002 []*πg.Object
		_ = πTemp002
		var πTemp003 *πg.Object
		_ = πTemp003
		var πTemp004 *πg.Object
		_ = πTemp004
		for ; πF.State() >= 0; πF.PopCheckpoint() {
			switch πF.State() {
			case 0:
			default: panic("unexpected function state")
			}
			// line 1: a = 'hello'
			πF.SetLineno(1)
			if πE = πF.Globals().SetItem(πF, ßa.ToObject(), ßhello.ToObject()); πE != nil {
				continue
			}
			// line 2: b = 'world'
			πF.SetLineno(2)
			if πE = πF.Globals().SetItem(πF, ßb.ToObject(), ßworld.ToObject()); πE != nil {
				continue
			}
			// line 3: message = ', '.join([a, b])
			πF.SetLineno(3)
			πTemp001 = πF.MakeArgs(1)
			πTemp002 = make([]*πg.Object, 2)
			if πTemp003, πE = πg.ResolveGlobal(πF, ßa); πE != nil {
				continue
			}
			πTemp002[0] = πTemp003
			if πTemp003, πE = πg.ResolveGlobal(πF, ßb); πE != nil {
				continue
			}
			πTemp002[1] = πTemp003
			πTemp003 = πg.NewList(πTemp002...).ToObject()
			πTemp001[0] = πTemp003
			if πTemp003, πE = πg.GetAttr(πF, πg.NewStr(", ").ToObject(), ßjoin, nil); πE != nil {
				continue
			}
			if πTemp004, πE = πTemp003.Call(πF, πTemp001, nil); πE != nil {
				continue
			}
			πF.FreeArgs(πTemp001)
			if πE = πF.Globals().SetItem(πF, ßmessage.ToObject(), πTemp004); πE != nil {
				continue
			}
			// line 4: print message
			πF.SetLineno(4)
			πTemp001 = make([]*πg.Object, 1)
			if πTemp003, πE = πg.ResolveGlobal(πF, ßmessage); πE != nil {
				continue
			}
			πTemp001[0] = πTemp003
			if πE = πg.Print(πF, πTemp001, true); πE != nil {
				continue
			}
		}
		return nil, πE
	})
	πg.RegisterModule("__main__", Code)
}
a = 'hello'
b = 'world'
message = ', '.join([a, b])
print message
var πR *πg.Object; _ = πR
var πE *πg.BaseException; _ = πE
ßa := πg.InternStr("a")
ßb := πg.InternStr("b")
ßhello := πg.InternStr("hello")
ßjoin := πg.InternStr("join")
ßmessage := πg.InternStr("message")
ßworld := πg.InternStr("world")
a = 'hello'
b = 'world'
message = ', '.join([a, b])
print message
// line 1: a = 'hello'
πF.SetLineno(1)
if πE = πF.Globals().SetItem(πF, ßa.ToObject(), ßhello.ToObject()); πE != nil {
	continue
}

// line 2: b = 'world'
πF.SetLineno(2)
if πE = πF.Globals().SetItem(πF, ßb.ToObject(), ßworld.ToObject()); πE != nil {
	continue
}
a = 'hello'
b = 'world'
message = ', '.join([a, b])
print message
// line 3: message = ', '.join([a, b])
πF.SetLineno(3)
πTemp001 = πF.MakeArgs(1)
πTemp002 = make([]*πg.Object, 2)
if πTemp003, πE = πg.ResolveGlobal(πF, ßa); πE != nil {
	continue
}
πTemp002[0] = πTemp003
if πTemp003, πE = πg.ResolveGlobal(πF, ßb); πE != nil {
	continue
}
πTemp002[1] = πTemp003
πTemp003 = πg.NewList(πTemp002...).ToObject()
πTemp001[0] = πTemp003
if πTemp003, πE = πg.GetAttr(πF, πg.NewStr(", ").ToObject(), ßjoin, nil); πE != nil {
	continue
}
if πTemp004, πE = πTemp003.Call(πF, πTemp001, nil); πE != nil {
	continue
}
πF.FreeArgs(πTemp001)
if πE = πF.Globals().SetItem(πF, ßmessage.ToObject(), πTemp004); πE != nil {
	continue
}
a = 'hello'
b = 'world'
message = ', '.join([a, b])
print message
// line 4: print message
πF.SetLineno(4)
πTemp001 = make([]*πg.Object, 1)
if πTemp003, πE = πg.ResolveGlobal(πF, ßmessage); πE != nil {
	continue
}
πTemp001[0] = πTemp003
if πE = πg.Print(πF, πTemp001, true); πE != nil {
	continue
}

GRUMPY INTERNALS

Stdlib

#lib/math.py

from '__go__/math' import (Pi, E, Ceil, ...)

def ceil(x):
    return Ceil(float(x))
  • CPython
  • PyPy
  • Ouroboros
  • Golang
  • third_party/stdlib/StringIO.py
  • third_party/pypy/datetime.py
  • third_party/ouroboros/operator.py
  • lib/math.py

GRUMPY INTERNALS

Grumpy Runtime

  • object
  • int
  • threading
// runtime/object.go
package grumpy

// Object represents Python 'object' objects.
type Object struct {
	typ  *Type `attr:"__class__"`
	dict *Dict
	ref  *WeakRef
}
// runtime/int.go
package grumpy

// Int represents Python 'int' objects.
type Int struct {
	Object
	value int
}
// runtime/threading.go
package grumpy

type TryableMutex struct {
	c chan bool
}

// Lock blocks until the mutex is available and then acquires a lock.
func (m *TryableMutex) Lock() {
	<-m.c
}
  • biblioteca unittest com funcionamento básico e rodando algumas suítes do CPython (alvo: MAR 2017)
  • Todos os benchmarks do CPython rodando (alvo: JUL 2017)
  • Todas as suítes de teste do CPython rodando sem erros
    (alvo: SET 2017)
  • Estratégia e design para suportar Python 3 (alvo: OUT 2017)
  • Suporte completo a Python 3 (alvo: JUL 2018)

Socorro: Fork Me

Perguntas

alan.justino@yahoo.com.br

@alanjds

?

Obrigado

alan.justino@yahoo.com.br

Perguntas:

Slides:

Python Runtime sem GIL em Golang: Grumpy

By alanjds

Python Runtime sem GIL em Golang: Grumpy

Palesta apresentada na PythonBrasil[13] em Belo Horizonte em outubro de 2017

  • 2,523