

<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Rodrigo Kumpera Weblog &#187; ruby</title>
	<atom:link href="http://www.kumpera.net/blog/index.php/category/ruby/feed/" rel="self" type="application/rss+xml" />
	<link>http://www.kumpera.net/blog</link>
	<description>Meus achados sobre tecnologia</description>
	<lastBuildDate>Thu, 10 Jun 2010 04:33:11 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Como fazer uma linguagem dinâmica ser rápida?</title>
		<link>http://www.kumpera.net/blog/index.php/2008/05/22/como-fazer-uma-linguagem-dinamica-ser-rapida/</link>
		<comments>http://www.kumpera.net/blog/index.php/2008/05/22/como-fazer-uma-linguagem-dinamica-ser-rapida/#comments</comments>
		<pubDate>Thu, 22 May 2008 05:09:16 +0000</pubDate>
		<dc:creator>kumpera</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[language design]]></category>
		<category><![CDATA[mono]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.kumpera.net/blog/index.php/2008/05/22/como-fazer-uma-linguagem-dinamica-ser-rapida/</guid>
		<description><![CDATA[Muito se fala em como as implementações de Ruby estão ficando rápidas, que estão evoluindo rapidamente. Porém não consigo pensar em como todas elas parecem mas preocupadas em repetir o caminho das pedras que outras linguagens dinâmicas passaram em décadas passadas.
Hoje a maioria ainda está no estágio de possuir um interpretador razoável e estar começando [...]]]></description>
			<content:encoded><![CDATA[<p>Muito se fala em como as implementações de Ruby estão ficando rápidas, que estão evoluindo rapidamente. Porém não consigo pensar em como todas elas parecem mas preocupadas em repetir o caminho das pedras que outras linguagens dinâmicas passaram em décadas passadas.</p>
<p>Hoje a maioria ainda está no estágio de possuir um interpretador razoável e estar começando a investir em algum mecanismo primitivo de compilação para código nativo. Talvez a única diferença seja o uso de inline caches para resolução de nomes, que foi uma contribuição significativa do pessoal do Self no começo dos anos 90. Porém os principais resultados obtivos por pesquisas a um bom tempo, como tracing, especialização e especulação não deram as caras ainda.</p>
<p>Inline caching é uma optimização no qual o resultado da resolução de nomes, para dispatch de método por exemplo, é armazenado entre ativações distintas. O truque é introduzir uma clausula de guarda que verifica o tipo do objeto seletor e sua versão. Caches devem ser invalidados sempre que alguém alterar uma classe. Temos dois tipos de cache, os monomórficos que fazem caching de apenas uma invocação e os polimórficos, que armazenam uma série de resultados. Para melhor vamos exemplificar com pseudo código:</p>
<p><code><br />
//ruby<br />
def test (a)<br />
    a.foo<br />
end</p>
<p>//pseudo-código gerado em C# para um cache monomórfico</p>
<p>class CallSite {<br />
    Type type;<br />
    MethodInfo method;<br />
    int version;<br />
}</p>
<p>static CallSite test_site_0;</p>
<p>object test(object a) {<br />
    //guarda do cache monomórfico verifica tipo e versão<br />
    if (test_site_0.type == a.Type &#038;&#038; test_site_0.version == A.Type.version)<br />
        return method (a);<br />
    else<br />
        //ResolveAndInvoke resolve o método "foo", invoca ele e atualiza o cache<br />
        return ResolveAndInvoke (a, "foo", ref test_site_0);<br />
}</p>
<p>//pseudo-código gerado em C# para um cache polimórfico<br />
delegate object InlineCacheNoArg (object this_);</p>
<p>static InlineCacheNoArg test_site_0;</p>
<p>object test(object a) {<br />
    return test_site_0 (a);<br />
}</p>
<p>//pseudo-código inicial do delegate de test_site_0<br />
object test_site_0_v0 (object _this) {<br />
    return ResolveAndInvoke (a, "foo", ref test_site_0);<br />
}</p>
<p>//pseudo-código do delegate depois de chamarmos test (99)<br />
object test_site_0_v1 (object _this) {<br />
    if (_this.Type == typeof (int) &#038;&#038; _this.version == 1)<br />
        //o dispatch aqui pode ser via um delegate dependendo da resolução<br />
        return ((int)_this).foo ();<br />
    return ResolveAndInvoke (a, "foo", ref test_site_0);<br />
}</p>
<p>//pseudo-código do delegate depois de chamarmos test ("str")<br />
object test_site_0_v2 (object _this) {<br />
    if (_this.Type == typeof (int) &#038;&#038; _this.version == 1)<br />
        return ((int)_this).foo ();<br />
    if (_this.Type == typeof (string) &#038;&#038; _this.version == 1)<br />
        return ((string)_this).foo ();<br />
    return ResolveAndInvoke (a, "foo", ref test_site_0);<br />
}<br />
</code></p>
<p>Como fica claro pelo exemplo, um cache polimórfico tem uma performance superior pois usa literais e funciona muito melhor no caso de não existir um tipo dominante entre as ativações. Aqui fica clara a limitação de uma das implementações, o JRuby não pode se dar ao luxo de gerar tantos métodos pois cada um precisa de uma classe e um classloader novos, ou seja, abusa da PermGen do Java.</p>
<p>Apesar de inline caches resultarem em ganho expressivo de performance, estão longe de produzirem algo razoável. O grande problema continua sendo o enorme custo de dispatch para código simples. A chave disso é fazer inferência dos tipos, de forma a conseguir eliminar por completo o overhead dos caches e resolução de nomes. As atuais implementações não implementam se quer inferência estática, ou seja, código como &#8220;123.to_s&#8221; é executado sem qualquer conhecimento prévio de 123.</p>
<p>O grande avanço ocorre se fizemos inferência em tempo de execução. Ou seja, instrumentamos o código para coletar os tipos que aparecem pelo código durante a execução e baseado nisso a máquina virtual gera versões mais eficientes do código. Existem duas técnicas bastante difundidas de como fazer isso, uma é via especialização parcial de métodos e a outra é via trace-based optimization.</p>
<p>Trace-based optimization é a tecnologia adotada pela Tamarin, a próxima VM de Javascript da Mozilla. De maneira sucinta, essa técnica consiste em gravar os trechos, e os tipos encontrados, que executam mais freqüentemente e gerar código eficiente baseado nessa informação. Os trechos gravados costumam incluir vários métodos diferentes e suas ativações juntas. Suas principais vantagens é a conseguir fazer inlining de métodos de maneira muito eficiente e normalmente gastar menos tempo no JIT. Porém existem uma enorme quantidade de problemas complexos de serem resolvidos como limitar expansão descontrolada da quantidade de código gerado.</p>
<p>Especialização parcial de métodos também leva em conta os métodos que executam mais freqüentemente. Os tipos dos parâmetros e valores de retorno<br />
são armazenados e posteriormente utilizados para gerar versões especializadas dos métodos em questão. Sua principal vantagem é a maior simplicidade<br />
e o fato de existir muito mais literatura e ferramentas para gerar código eficiente nestes casos. Para se ter uma idéia do poder dessa técnica um exemplo cai bem:</p>
<p><code><br />
//ruby<br />
def fun (a, b)<br />
    2 * a - b<br />
end</p>
<p>//fun é sempre chamada com números como argumento, "fun (1,2)" por exemplo.</p>
<p>//pseudo-código C# gerado inicialmente (sem usar caches)<br />
object fun_v0 (object a, object b) {<br />
    object tmp = Invoke (2, "*", a"); //_this, nome do método, argumentos<br />
    return Invoke (tmp, "-", b);<br />
}</p>
<p>//pseudo-ćodigo C# gerado após especialização:<br />
int fun_int_int (int a, int b) {<br />
    int tmp = 2 * a; //o método que implementava multiplicação foi inlined<br />
    return tmp - b;<br />
}</p>
<p>object fun_v1 (object a, object b) {<br />
    if (a.Type == typeof (int) &#038;&#038; b.Type == typeof (int) &#038;&#038; typeof (int).version == 1)<br />
        return fun_int_int ((int)a, (int)b);<br />
    object tmp = Invoke (2, "*", a"); //_this, nome do método, argumentos<br />
    return Invoke (tmp, "-", b);<br />
}<br />
</code></p>
<p>Não precisa ir muito longe para imaginar a diferença de performance entre as duas versões. Porém entre descobrir os métodos as serem especializados e gerar código de máquina existe um Just-In-Time compiler que é, surpresa, surpresa, muito trabalhoso de ser escrito para executar rapidamente e gerar código eficiente.</p>
<p>Possui um bom JIT é atualmente um grande dilema entre as implementações de ruby. Pois ao usar o JIT de máquinas virtuais maduras, como HotSpot ou mono, a implementação fica limitada ao que é possível a linguagens gerenciadas. Em contrapartida, ao não utilizá-las, construções de baixo nível como profiler e interpretador podem ser implementadas de forma muito mais eficiente.</p>
<p>Apesar de do futuro parecer muito legal, futuras VMs terão o trabalho adicional de educar a comunidade de desenvolvedores sobre como escrever código rápido em ruby. Coisas como monkey patching furam qualquer esquema de caching ou especialização pois cada objeto passa a ter uma singleton class distinta.</p>
<p>Não existe solução fácil e todas implementações tem muito chão pela frente até Ruby ter uma performance competitiva com outras linguagens dinâmicas. Soluções como as aqui apresentadas devem conseguir melhorias de uma ordem de magnitude tranquilamente, de acordo com os resultados já encontrados. Performance é um problema que é resolvido via muito suor, com uma contínua série de melhoras, atacando um pouco por vez.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kumpera.net/blog/index.php/2008/05/22/como-fazer-uma-linguagem-dinamica-ser-rapida/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Monkeypatching: gambiarra du-jour</title>
		<link>http://www.kumpera.net/blog/index.php/2008/03/03/monkeypatching-gambiarra-du-jour/</link>
		<comments>http://www.kumpera.net/blog/index.php/2008/03/03/monkeypatching-gambiarra-du-jour/#comments</comments>
		<pubDate>Mon, 03 Mar 2008 14:20:00 +0000</pubDate>
		<dc:creator>kumpera</dc:creator>
				<category><![CDATA[Programming]]></category>
		<category><![CDATA[java]]></category>
		<category><![CDATA[language design]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.kumpera.net/blog/index.php/2008/03/03/monkeypatching-gambiarra-du-jour/</guid>
		<description><![CDATA[Finalmente o óbvio atingiu a comunidade de desenvolvedores de Ruby. Enfim descobriram que meta-programação sem disciplina é um engôdo. No começo produz resultados fabulosos rapidamente, só que mais adiante se torna um inferno de integração. Não falta gente calejada em Rails para te contar uma miríade de problemas ao integrar frameworks que modificam classes como [...]]]></description>
			<content:encoded><![CDATA[<p>Finalmente o óbvio atingiu a comunidade de desenvolvedores de Ruby. Enfim descobriram que meta-programação sem disciplina é um engôdo. No começo produz resultados fabulosos rapidamente, só que mais adiante se torna um inferno de integração. Não falta gente calejada em Rails para te contar uma miríade de problemas ao integrar frameworks que modificam classes como Object ou Fixnum.</p>
<p>Sistemas grandes e complexos exigem que seus componentes sejam isolados o máximo possível entre si para minimizar a interferência que um pode causar no outro. Em linguagens como Java, com um sistema de tipos simples, normalmente isso se resume a usar interfaces para delimitar fronteiras e protocolos de comunicação inter-módulo. Porém quando temos classes abertas o problema é muito maior, já que o sistema todo pode ser modificado de um único lugar. Permitir que a modificação de classes fundamentais como Kernel em um canto do sistema influencie todo o resto é um sério problema pois fica difícil controlar o estrago que essas mudanças causam.</p>
<p>O problema com Ruby pode ser entendido melhor se olharmos para ele segundo o a classificação de <a href="http://fragmental.tw/research-on-dsls/language-oriented-programming-lop/">domínios de page-jones</a>. Segundo o mesmo, podemos classificá-los em fundamental, arquitetural, negócios e aplicação; sendo que a especificidade aumenta na mesma ordem. Outro ponto é que o domínio menos específico não deve depender de um mais específico, assim como dependências laterais devem ser evitadas. Por essa ótica, código da aplicação alterando uma classe fundamental do sistema é uma clara violação desse modelo.</p>
<p>O motivo pelo qual Ruby viola o modelo de page-jones é por não ser possível definir um escopo que não seja global para alterações feitas a classes externas a código em questão. Outro problema que agrava a situação é a impossibilidade de realmente controlar o mecanismo de resolução de nomes de um bloco de código, isso é um grande problema para criação de DSLs que acabam por fazer enormes cirurgias no core da linguagem ou apelam para o monkeypatching, uma &#8220;solução&#8221; muito menos intrusiva.</p>
<p>Existem duas formas de ser realizar meta-programação; uma é a intrínseca, a qual se permite operar sobre a definição dos tipos e, uma vez feita a alteração, ela é visível a todos usuários do dado tipo, podemos dizer também que é meta-programação no ponto de definição; a outra é a extrínseca, na qual se altera o mecanismo de resolução de nomes para um dado corpo de código, isso permite realizar as mesmas operações que o método anterior, porém todo código precisa informar direta ou indiretamente se existe algo alterando tal mecanismo, podemos dizer também que é meta-programação no ponto de uso.</p>
<p>A vantagem do primeiro é simplicidade e alcance, uma vez feita a modificação nada mais precisa ser feito para sua aplicação utilizar a versão modificada do tipo, porém também é sua fraqueza, pois se uma função depender explicitamente do comportamento anterior ela não mais funcionará &#8211; Ruby é um ótimo exemplo de meta-programação intrínseca. Já a segunda técnica é quase o oposto, pois exige que as modificações sejam explicitamente ativadas, que é sua principal vantagem, pois permite facilmente compor conjuntos de alterações e restringir seu escopo, em contrapartida é sua fraqueza pois não permite que uma dada alteração seja definida por completo em um único lugar &#8211; extension methods do C# 3.0 é um exemplo simples de meta-programação extrínseca.</p>
<p>Tentar comparar ambas as técnicas e concluir qual a melhor é um exercício fútil, pois sempre existirão fartos casos no qual uma falha e a outra brilha. Assim como achar que Ruby é uma linguagem quebrada por possuir seus defeitos, pois apesar de tudo é um modelo ao mesmo tempo muito rico e simples de usar.</p>
]]></content:encoded>
			<wfw:commentRss>http://www.kumpera.net/blog/index.php/2008/03/03/monkeypatching-gambiarra-du-jour/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
	</channel>
</rss>
