TDC2016POA | Trilha Ruby - Stack Level too Deep e Tail Call Optimization: É uma boa ideia fazer recursão em Ruby?

Embed Size (px)

Citation preview

Java para Lderes e Gerentes

Trilha Ruby

Guilherme Baptista

10 anos deixando linhas de
cdigo por a em lugares como:

Stack Level too Deep
Tail Call Optimization

uma boa ideia fazer recurso em Ruby?

Familiar?

file.rb:10:in `call':stack level too deep (SystemStackError)from file.rb:10:in `'

Qual o significado?

file.rb:10:in `call':stack level too deep (SystemStackError)from file.rb:10:in `'

Qual o significado?

file.rb:10:in `call':stack level too deep (SystemStackError)from file.rb:10:in `'

stack overflow?

file.rb:10:in `call':stack level too deep (SystemStackError)from file.rb:10:in `'

stack overflow?

?

file.rb:10:in `call':stack level too deep (SystemStackError)from file.rb:10:in `'

O que ?

uma proteo contra loops infinitos?

uma proteo contra loops infinitos?

uma proteo contra loops infinitos?

uma proteo contra loops infinitos?

uma proteo contra loops infinitos?

um erro causado por uso de memria em excesso?

um erro causado por uso de memria em excesso?

um erro causado por uso de memria em excesso?

um erro causado por uso de memria em excesso?

file.rb:8:in `':failed to allocate memory (NoMemoryError)

Bnus: Uma linha para acabar com a memria RAM em segundos:

Obs.: No rodar no irb do seu servidor em produo... #fikdik

uma proteo para no deixar um mtodo chamar a si mesmo?

uma proteo para no deixar um mtodo chamar a si mesmo?

uma proteo para no deixar um mtodo chamar a si mesmo?

Done!

uma proteo para no deixar um mtodo chamar a si mesmo muitas vezes?

uma proteo para no deixar um mtodo chamar a si mesmo muitas vezes?

uma proteo para no deixar um mtodo chamar a si mesmo muitas vezes?

Done!

uma proteo para no deixar um mtodo chamar a si mesmo infinitamente?

uma proteo para no deixar um mtodo chamar a si mesmo infinitamente?

file.rb:2:in `me_myself_and_i': stack level too deep (SystemStackError) from file.rb:2:in `me_myself_and_i' from file.rb:2:in `me_myself_and_i' from file.rb:2:in `me_myself_and_i' from file.rb:2:in `me_myself_and_i' ... 11901 levels... from file.rb:2:in `me_myself_and_i' from file.rb:2:in `me_myself_and_i' from file.rb:2:in `me_myself_and_i' from file.rb:5:in `'

Ser?

5

1 + 1 + 1 + 1 + 1

5

1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1

20000

20000

file.rb:7:in `eval': stack level too deep (SystemStackError) from file.rb:7:in `'

uma proteo para no deixar um mtodo chamar a si mesmo infinitamente?

file.rb:7:in `eval': stack level too deep (SystemStackError) from file.rb:7:in `'

Pode no ser bem isso...

Vamos falar sobre pilha:

Stack(abstract data type)

LIFO: last-in, first-out

(o ltimo que entra o primeiro que sai)

Stack (LIFO: last-in, first-out)

Stack

Stack (LIFO: last-in, first-out)

Stack (LIFO: last-in, first-out)

Stack (LIFO: last-in, first-out)

Stack (LIFO: last-in, first-out)

Stack (LIFO: last-in, first-out)

Stack (LIFO: last-in, first-out)

Stack (LIFO: last-in, first-out)

Em Ruby tudo objeto.

RubyVM::InstructionSequence

RubyVM::InstructionSequence.of

Proc

Ou

method

Ruby Code

down to

VM Instructions

YARB\x02\x00\x00\x00\x03\x00\x00\x00r\x02\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x04\x00\x00\x00\xF9\x01\x00\x00\xFD\x01\x00\x00b\x02\x00\x00x86_64-linux\x00*\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00*\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00Z\x00\x00\x00\x00\x00\x00\x00Z\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Z\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00Z\x00\x00\x00\x00\x00\x00\x00;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00*\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x00\x00\x00\x001\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\v\x00\x00\x00\x02\x00\x00\x00\r\x00\x00\x00\x11\x00\x00\x00\x0F\x00\x00\x00\x13\x00\x00\x00\r\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x10\x00\x00\x00\x01\x00\x00\x00\x01\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x14\x00\x00\x009\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x17\x00\x00\x00\x00\x00\x00\x00\xD9\x00\x00\x00\x00\x00\x00\x00\xF9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xF9\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00)\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x03\x00\x00\x00\x00\x00\x00\x00\xF1\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x00E\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x05\x00\x00\x00\x00\x00\x00\x00(irb)E\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\a\x00\x00\x00\x00\x00\x00\x00the_sumE\x00\x00\x00\x02\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00+\r\x02\x00\x00\x19\x02\x00\x002\x02\x00\x00M\x02\x00\x00

== disasm: #========================================0000 trace 8 ( 54)

0002 trace 1 ( 55)

0004 putobject_OP_INT2FIX_O_1_C_

0005 putobject_OP_INT2FIX_O_1_C_

0006 opt_plus ,

0009 putobject_OP_INT2FIX_O_1_C_

0010 opt_plus ,

0013 putobject_OP_INT2FIX_O_1_C_

0014 opt_plus ,

0017 trace 16 ( 56)

0019 leave ( 55)

putobject_OP_INT2FIX_O_1_C_

putobject_OP_INT2FIX_O_1_C_

opt_plus p3

putobject_OP_INT2FIX_O_1_C_

putobject_OP_INT2FIX_O_1_C_

opt_plus n3 > p2 > n4 > p3

putobject_OP_INT2FIX_O_1_C_

putobject_OP_INT2FIX_O_1_C_

opt_plus n3 > p2 > n4 > p3

putobject_OP_INT2FIX_O_1_C_

putobject_OP_INT2FIX_O_1_C_

opt_plus n3 > p2 > n4 > p3

Recurso?

Recurso:

Capacidade que uma rotina (funo ou mtodo) possui de invocar a si mesma.

Recurso:

Condio de parada:

Hi Fulano!

Hi Fulano!

Hi Fulano!

Recurso vs Iterao

Como contar a quantidade de itens em uma lista?

Array:

Iterao:

Iterao:

Array:

4

Array:

4

Iterao:

Iterao:

Array:

4

Iterao:

Array:

4

Transformar todas as letras em maisculas:

["A", "B", "C", "D"]

Mas no dia a dia no fazemos exatamente assim...

Como realmente fazemos:

E com recurso?

Head / Tail

A
Head

Tail

B
Head

A
Head

Tail

A
Head

Tail

A
Head

Tail

Como contar a quantidade de itens em uma lista?

count(["A", "B", "C", "D"])

count(["B", "C", "D"])

count(["C", "D"])

count(["D"])

count([])

1 + count(["B", "C", "D"])

1 + 1 + count(["D", "D"])

1 + 1 + 1 + count(["D"])

1 + 1 + 1 + 1 + count([])

1 + 1 + 1 + 1 + 0

4

Como somar todos os itens de uma lista?

1 + sum([2, 2, 2])

1 + 2 + sum([2, 2])

1 + 2 + 2 + sum([2])

1 + 2 + 2 + 2 + sum([])

1 + 2 + 2 + 2 + 0

7

E por a vai...

Stack Overflow

Stack Overflow1 + count(["B", "C", "D"])

1 + count(["C", "D"])

1 + count(["D"])

1 + count([])

0

Tamanho do seu Stack

Stack Overflow1 + count(["B", "C", "D"])

1 + count(["C", "D"])

1 + count(["D"])

1 + count([])

0

Stack Overflow

Stack Overflow1 + count(["B", "C", "D"])

1 + count(["C", "D"])

1 + count(["D"])

1 + count([])

0

stack level too deep
(SystemStackError)

Stack Overflowfile.rb:7:in `eval': stack level too deep (SystemStackError) from file.rb:7:in `'

Stack Overflowstack level too deep
(SystemStackError)

s1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 +

E a?

Opo 1:Aumentar o tamanho do Stack

ulimit -allcore file size (blocks, -c) 0data seg size (kbytes, -d) unlimitedscheduling priority (-e) 0file size (blocks, -f) unlimitedpending signals (-i) 31321max locked memory (kbytes, -l) 64max memory size (kbytes, -m) unlimitedopen files (-n) 1024pipe size (512 bytes, -p) 8POSIX message queues (bytes, -q) 819200real-time priority (-r) 0stack size (kbytes, -s) 8192cpu time (seconds, -t) unlimitedmax user processes (-u) 31321virtual memory (kbytes, -v) unlimitedfile locks (-x) unlimited

RubyVM::DEFAULT_PARAMS{

thread_vm_stack_size: 1048576,

thread_machine_stack_size: 1048576,

fiber_vm_stack_size: 131072,

fiber_machine_stack_size: 524288

}

Opo 2:Tail Call Optimization

cTail Call Optimization1 + count(["B", "C", "D"])

1 + count(["C", "D"])

1 + count(["D"])

1 + count([])

0

stack level too deep
(SystemStackError)

c

Tail Call Optimization

count(["A", "B", "C", "D"], 0)

count(["B", "C", "D"], 1)

count(["C", "D"], 2)

count(["D"], 3)

count([], 4)

4

Tamanho do seu Stack

Reaproveitamento do seu Stack

file.rb:11:in `count': stack level too deep (SystemStackError)

from file.rb:11:in `count'

from file.rb:11:in `count'

from file.rb:11:in `count'

from file.rb:11:in `count'

from file.rb:11:in `count'

from file.rb:11:in `count'

from file.rb:11:in `count'

from file.rb:11:in `count'

... 8723 levels...

from file.rb:11:in `count'

from file.rb:11:in `count'

from file.rb:11:in `count'

from file.rb:17:in `'

Precisamos compilar o nosso cdigo com instrues especficas para a RubyVM / YARV

== disasm: #=====================================local table (size: 5, argc: 1 [opts: 1, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])

[ 5] list [ 4] acc [ 3] head [ 2] tail

0000 putobject_OP_INT2FIX_O_0_C_ ( 4)

0001 setlocal_OP__WC__0 4

0003 trace 8

0005 trace 1 ( 6)

0007 getlocal_OP__WC__0 5

0009 opt_empty_p ,

0012 branchunless 21

0014 nop

0015 nop

0016 getlocal_OP__WC__0 4

0018 trace 16

0020 leave

0021 trace 1 ( 8)

0023 getlocal_OP__WC__0 5

0025 opt_send_without_block ,

0028 setlocal_OP__WC__0 3

0030 trace 1 ( 9)

0032 getlocal_OP__WC__0 5

0034 setlocal_OP__WC__0 2

0036 trace 1 ( 11)

0038 putself

0039 getlocal_OP__WC__0 2

0041 getlocal_OP__WC__0 4

0043 putobject_OP_INT2FIX_O_1_C_

0044 opt_plus ,

0047 opt_send_without_block ,

0050 trace 16 ( 13)

0052 leave ( 11)

== disasm: #=====================================local table (size: 5, argc: 1 [opts: 1, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])

[ 5] list [ 4] accc [ 3] head [ 2] tail

0000 putobject_OP_INT2FIX_O_0_C_ ( 4)

0001 setlocal_OP__WC__0 4

0003 trace 8

0005 trace 1 ( 6)

0007 getlocal_OP__WC__0 5

0009 opt_empty_p ,

0012 branchunless 21

0014 nop

0015 nop

0016 getlocal_OP__WC__0 4

0018 trace 16

0020 leave

0021 trace 1 ( 8)

0023 getlocal_OP__WC__0 5

0025 opt_send_without_block ,

0028 setlocal_OP__WC__0 3

0030 trace 1 ( 9)

0032 getlocal_OP__WC__0 5

0034 setlocal_OP__WC__0 2

0036 trace 1 ( 11)

0038 putself

0039 getlocal_OP__WC__0 2

0041 getlocal_OP__WC__0 4

0043 putobject_OP_INT2FIX_O_1_C_

0044 opt_plus ,

0047 opt_send_without_block ,

0050 trace 16 ( 13)

0052 leave ( 11)

Acompanhar eventos durante a execuo do cdigo internamente na VMstackprof ruby-prof

trace + tail call optimization

=

== disasm: #=====================================local table (size: 5, argc: 1 [opts: 1, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])

[ 5] list [ 4] acc [ 3] head [ 2] tail

0000 putobject_OP_INT2FIX_O_0_C_ ( 2)

0001 setlocal_OP__WC__0 4

0003 getlocal_OP__WC__0 5 ( 3)

0005 opt_empty_p ,

0008 branchunless 15

0010 nop

0011 nop

0012 getlocal_OP__WC__0 4

0014 leave

0015 getlocal_OP__WC__0 5 ( 5)

0017 opt_send_without_block ,

0020 setlocal_OP__WC__0 3

0022 getlocal_OP__WC__0 5 ( 6)

0024 setlocal_OP__WC__0 2

0026 putself ( 8)

0027 getlocal_OP__WC__0 2

0029 getlocal_OP__WC__0 4

0031 putobject_OP_INT2FIX_O_1_C_

0032 opt_plus ,

0035 opt_send_without_block ,

0038 leave

== disasm: #=====================================local table (size: 5, argc: 1 [opts: 1, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])

[ 5] list [ 4] acc [ 3] head [ 2] tail

0000 putobject_OP_INT2FIX_O_0_C_ ( 2)

0001 setlocal_OP__WC__0 4

0003 getlocal_OP__WC__0 5 ( 3)

0005 opt_empty_p ,

0008 branchunless 15

0010 nop

0011 nop

0012 getlocal_OP__WC__0 4

0014 leave

0015 getlocal_OP__WC__0 5 ( 5)

0017 opt_send_without_block ,

0020 setlocal_OP__WC__0 3

0022 getlocal_OP__WC__0 5 ( 6)

0024 setlocal_OP__WC__0 2

0026 putself ( 8)

0027 getlocal_OP__WC__0 2

0029 getlocal_OP__WC__0 4

0031 putobject_OP_INT2FIX_O_1_C_

0032 opt_plus ,

0035 opt_send_without_block ,

0038 leave

== disasm: #=====================================local table (size: 5, argc: 1 [opts: 1, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])

[ 5] list [ 4] acc [ 3] head [ 2] tail

0000 putobject_OP_INT2FIX_O_0_C_ ( 2)

0001 setlocal_OP__WC__0 4

0003 getlocal_OP__WC__0 5 ( 3)

0005 opt_empty_p ,

0008 branchunless 15

0010 nop

0011 nop

0012 getlocal_OP__WC__0 4

0014 leave

0015 getlocal_OP__WC__0 5 ( 5)

0017 opt_send_without_block ,

0020 setlocal_OP__WC__0 3

0022 getlocal_OP__WC__0 5 ( 6)

0024 setlocal_OP__WC__0 2

0026 putself ( 8)

0027 getlocal_OP__WC__0 2

0029 getlocal_OP__WC__0 4

0031 putobject_OP_INT2FIX_O_1_C_

0032 opt_plus ,

0035 opt_send_without_block ,

0038 leave

== disasm: #=====================================local table (size: 5, argc: 1 [opts: 1, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])

[ 5] list [ 4] acc [ 3] head [ 2] tail

0000 putobject_OP_INT2FIX_O_0_C_ ( 2)

0001 setlocal_OP__WC__0 4

0003 getlocal_OP__WC__0 5 ( 3)

0005 opt_empty_p ,

0008 branchunless 15

0010 nop

0011 nop

0012 getlocal_OP__WC__0 4

0014 leave

0015 getlocal_OP__WC__0 5 ( 5)

0017 opt_send_without_block ,

0020 setlocal_OP__WC__0 3

0022 getlocal_OP__WC__0 5 ( 6)

0024 setlocal_OP__WC__0 2

0026 putself ( 8)

0027 getlocal_OP__WC__0 2

0029 getlocal_OP__WC__0 4

0031 putobject_OP_INT2FIX_O_1_C_

0032 opt_plus ,

0035 opt_send_without_block ,

0038 leave

== disasm: #=====================================local table (size: 5, argc: 1 [opts: 1, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])

[ 5] list [ 4] acc [ 3] head [ 2] tail

0000 putobject_OP_INT2FIX_O_0_C_ ( 2)

0001 setlocal_OP__WC__0 4

0003 getlocal_OP__WC__0 5 ( 3)

0005 opt_empty_p ,

0008 branchunless 15

0010 nop

0011 nop

0012 getlocal_OP__WC__0 4

0014 leave

0015 getlocal_OP__WC__0 5 ( 5)

0017 opt_send_without_block ,

0020 setlocal_OP__WC__0 3

0022 getlocal_OP__WC__0 5 ( 6)

0024 setlocal_OP__WC__0 2

0026 putself ( 8)

0027 getlocal_OP__WC__0 2

0029 getlocal_OP__WC__0 4

0031 putobject_OP_INT2FIX_O_1_C_

0032 opt_plus ,

0035 opt_send_without_block ,

0038 leave

1000000

Por qu?

Mais rpido que iterar?

user system total real .each: 16.480000 0.000000 16.480000 ( 16.492415)recursion: 26.280000 0.380000 26.660000 ( 26.652826)

Maior proximidade com conceitos delinguagens funcionais

Considerada uma forma elegante de resolver problemas

Quicksort em C utilizando iterao

Quicksort em Haskell utilizando recurso

Maneira de lidar com diferentesestruturas de dados

Listas Ligadas

Grafos

rvores Binrias

Mais uma maneira de resolver problemas e entender os detalhes da linguagem

Referncias:

- RubyVM::InstructionSequence http://ruby-doc.org/core-2.3.1/RubyVM/InstructionSequence.html

- Inline caching in MRI http://tenderlovemaking.com/2015/12/23/inline-caching-in-mri.html

- Ruby, Trace, Leave, Oh my! http://kgrz.io/2014/04/19/ruby-trace-leave-oh-my.html

Referncias:

- Tail Call Optimization in Ruby http://nithinbekal.com/posts/ruby-tco/

- Tailin' Ruby http://timelessrepo.com/tailin-ruby

- Tail Call Optimization in Ruby: Deep Dive http://blog.tdg5.com/tail-call-optimization-in-ruby-deep-dive/

Referncias:

- TCOMethod Simplifies compiling code with tail call optimization in MRI Ruby

https://github.com/tdg5/tco_method

- Hamster Efficient, Immutable, Thread-Safe Collection classes for Ruby

https://github.com/hamstergem/hamster

- Functional Ruby A gem for adding functional programming tools to Ruby. Inspired by Erlang, Clojure, Haskell, and Functional Java.

https://github.com/jdantonio/functional-ruby

Obrigado!

gbaptista.com.br

Twitter: @guilhermebps

github.com/gbaptista

linkedin.com/in/guilhermebaptista

Globalcode Open4education

Globalcode Open4education