terça-feira, 14 de outubro de 2008

Discussion about "Challenges: An Application Model for Pervasive Computing"

The Pervasive Computing paradigm aims at connecting devices and providing services anytime, anywhere, which is considered to be an evolution of Mobile Computing and Distributed System areas. However, the process of developing, deploying and using nowadays pervasive software does not match user's interesting and does not make use of the whole potential features provided by pervasive computing. As a consequence, researchers have been studied new software development processes to meet user expectations and make features such as invisibility, heterogeneity and smartness really become a reality.

In this context, the paper "Challenges: An Application Model for Pervasive Computing" [1] gives a new vision about pervasive computing considering how pervasive applications, devices and environment must be understood and interconnected to achieve the whole potential of pervasive computing. First, it is about how people view mobile computing devices and use them to exploit and perform tasks in the environment; second, it is about how applications are created and deployed to help users perform this task and, finally, the third goal is about how environment can support new ubiquity information and technology. This point of view enforces us to see devices and environment in a different way. Devices are front-end for an application/data access, not a repository of software; a user must perform his/her task through the application, since it can not have a goal of exploiting devices' capabilities; and the environment must be enhanced of information, not a virtual space for executing and storing applications.

Based on this such point of view about devices, applications and environment, the paper proposes a new model for pervasive software development considering it's life-cycle, which is divided into three steps: (1) design-time - where the developer creates, maintain and enhances the application -, (2) load-time - where the system composes, adapts and loads the application components into an application instance on particular hardware devices and (3) run-time - where the user performs tasks using the installed application, which run in the environment and is adapted depending on the context available in the environment. Therefore, to achieve this goal, it is needed to make some improvements on today's devices, networking and programming standards, despite the problem covers more improvements than those just cited before.

Finally, the paper contributes with a model for pervasive software development, allowing software engineers achieve the whole potential of pervasive systems through a device-independent application development process, dynamic load-time system, which provides discovery and negotiation capabilities and a powered run-time system, which allows resources to be dynamically shared. Despite of the paper had been written in the beginning of the 21st century, the authors did not address how this model can be achieved through concrete technology examples or, in other words, how available technologies could be used to achieve non independent device application, for instance. And, it is needless to say that the authors goal exceeds the current state of the art.

Thiago Sales

[1] G. Banavar, J. Beck, E. Gluzberg, J. Munson, J. Sussman, D. Zukowski, Challenges: An Application Model for Pervasive Computing, Proceedings of the Sixth Annual International Conference on Mobile Computing and Networking, 2000, pp.266-274.

quarta-feira, 17 de setembro de 2008

Finalmente um "não paper"

Olá pessoal,
dessa vez não comentarei sobre papers. Apenas para disponibilizar
outro blog para aqueles interessados em desenvolvimento para aplicações
móveis com J2ME - http://efforts.embedded.ufcg.edu.br/javame/
... ou para aplicações com outras linguagens de programação para dispositivos móveis: http://efforts.embedded.ufcg.edu.br/

Abraços!!

terça-feira, 2 de setembro de 2008

Contextualizando protocolos de transporte de dados na Internet - Um foco no DCCP

Nos últimos anos, houve uma extraordinária disseminação das aplicações de rede "sensíveis ao atraso", tais como videoconferência, VoIP, jogos massivamente online, entre outros. Tais aplicações são caracterizadas pela preferência no tempo de entrega dos dados em detrimento da confiabilidade desta entrega. Como consequência, pode-se observar um intenso crescimento no uso do protocolo UDP [1] como solução paliativa para o transporte de dados multimídia na Internet devido a algumas características intrísicas relacionadas ao transporte de dados presentes - ou mesmo a falta de serviços para oferecer a estes tipos de aplicações - neste protocolo. Esta escolha é considerada plausível pois está estreitamente associada ao comportamento do protocolo TCP[2]. Nesta vertente, mecanismos de retransmissões de dados presentes no protocolo supracitado faz com que aplicações neste domínio recebam dados que, em um determinado momento, não são mais de seus interesses (pacotes atrasados, por exemplo).

O uso do protocolo UDP traz alguns impactos significativos quando utilizado em aplicações multimídia. Tais consequências estão diretamente relacionadas ao estado em que a rede pode atingir. Como o protocolo UDP não possui mecanismos de controle de congestionamento, fluxos UDP podem degradar consideravelmente a performance de outros protocolos, como, por exemplo, o do TCP, causando também excessiva perdas de pacotes. Como este protocolo fornece controle de congestionamento, ao detectar intensivo tráfego, o protocolo TCP tende a diminuir sua taxa de transmissão. Este cenário pode ser observado na Figura abaixo, onde um fluxo TCP concorre com três fluxos UDP. Pode-se observar que após 50 segundos do início do experimento, o fluxo UDP ocupa toda a largura de banda disponível na rede.



Fonte: [6]

Neste contexto, é notória a necessidade de um protocolo que não ofereça confiabilidade (ou seja, preferencialmente não faça uso de mecanismos de retransmissão), porém mantenha conhecimento do estado da rede, como, por exemplo, fazendo uso de mecanismos de controle de congestionamento, evitando que a mesma entre em alto nível de congestionamento quando defrontada com aplicações multimídia. Desta forma, a IETF (Internet Engeneering Task Force) [3] aprovou recentemente a especificação do protocolo DCCP (Datagram Congestion Control Protocol)[4][5] como um dos padrões de transmissão de dados multimídia para Internet. Esta decisão fundamentou-se objetivando melhorias nos serviços oferecidos pelas aplicações multimídia e pela escassez nas possibilidades de protocolos para o transporte de dados na Internet (TCP ou UDP).

Thiago Sales

[1] RFC0768. UDP: User Datagram Protocol, 8 1980. http://www.ietf.org/rfc/rfc0768.txt. Último acesso, Agosto de 2008.

[2] TCP: Tranmission Control Protocol, 9 1981. http://www.ietf.org/rfc/rfc0793.txt. Último acesso, Agosto de 2008.

[3] The Internet Engeneering Task Force, 1992. http://ietf.org

[4] E. Kohler, M. Handley, and S. Floyd. Datagram congestion control protocol
(dccp), 2006. http://www.rfc-editor.org/rfc/rfc4340.txt.

[5] Eddie Kohler, Mark Handley, and Sally Floyd. Designing DCCP: Congestion
Control Without Reliability. In in Proc. ACM SIGCOMM 2006, Piza, 2003

[6] Leandro Melo de Sales. Avaliação experimental do protocolo dccp para transmissão de conteúdos multimídia em redes sem fio 802.11g e na internet. Dissertação de Mestrado, Universidade Federal de Campina Grande, 2008.

quinta-feira, 7 de agosto de 2008

Análise e Crítica: "Inversion of Control Containers and the Dependency Injection Pattern"

O fraco acoplamento é um dos elementos mais cruciais no desenvolvimento de software orientado a objetos, pois este permite que a mudança em um objeto não exija mudanças em outros objetos. Este problema ocorre quando a mudança de implementação em um objeto "A" força mudanças em um objeto "B", onde normalmente o objeto "A" é o responsável pela criação do objeto "B" (forte acoplamento). Assim, a manutenção do software, reúso, escalabilidade e testes passam a ser tarefas difíceis de serem concretizadas. Além disso, localizar serviços independente da implementação é importante para cumprir com o princípio supracitado.

Neste contexto, Fowler[1] apresenta como os padrões "Dependency Injection" (ou Inversão de Controle) e "Service Locator" podem desacoplar objetos fortemente relacionados e corrigir independências entre objetos. No primeiro caso, é feita a utilização dos princípios de orientação a objetos tais como interface, herança e polimorfismo. A utilização de construtores, interfaces (menos utilizada) ou métodos set(s) permitem "injetar" - através do "assembler" - um objeto "B" à um objeto "A" que precise usufruir de serviços disponibilizados pelo objeto "B". Desta forma, o objeto "A" não precisa mais ter conhecimento dos atributos necessários para a criação do objeto "B", permitindo baixo acoplamento. No segundo caso, a idéia básica é ter um objeto (localizador) responsável por registrar serviços e associá-lo à uma chave. Um objeto "A" deve apenas recuperar uma instância do objeto "B" através deste localizador de serviços, passando a chave (normalmente em string) que esta instância está associada.

Fowler também discute quando utilizar as abordagens apresentadas. Ambas as abordagens propõem o baixo acoplamento, porém, a grande diferença entre elas está em como a implementação do objeto é disponibilizada ao objeto que deseja utilizá-lo. Por um lado, ao utilizar "Service Locator", uma requisição explícita ao responsável pela localização do serviço deve ser realizada, mantendo uma dependência com este objeto localizador. Por outro lado, na inversão de controle, não é realizada nenhuma requisição, visto que um objeto "A" recebe uma referência de um objeto "B" diretamente de construtores ou métodos set. Normalmente é mais difícil de entender e leva a problemas relacionados a debug. Desta forma, a escolha por qual das abordagens reflete na observação de onde a dependência se encontra.

Finalmente, os padrões permitem um melhor design, facilitando o reúso, o fraco acoplamento, e testes de componentes de software. Entretanto, acho que (1) a utilização de métodos get e set expõem os atributos de um objeto, violando o encapsulamento. Além disso, (2) acho que a utilização de construtores para injetar objetos pode tornar a inicialização de um objeto bastante confusa devido ao número de parâmetros que devem ser referenciados, apesar de "aliviar" o número de métodos set na classe. Entretanto, acho as abordagens plausíveis no que tange o fraco acoplamento entre objetos e a localização de serviços independentemente da implementação do mesmo.

Thiago Sales

[1] Martin Fowler. Inversion of control containers and the dependecy injection pattern. www.martinfowler.com, Janeiro 2004.

quarta-feira, 6 de agosto de 2008

Análise e Crítica: "Software and the Concurrency Revolution'' e ``The Problem with Threads

A programação concorrente é uma dos pontos chaves a qual faculta o paralelismo de tarefas e, em tese, possibilita o aumento na performance de uma aplicação. Entretanto, as atuais soluções para programação paralela traz ao programador problemas sérios às suas aplicações. Threads e sincronização de blocos, apesar de dominar as abordagens para programação concorrente, tendem a ser na maioria das vezes bastante confuso e de grande risco devido ao não determinismo associado. Além disso, o uso de "locks" para acesso concorrente a dados aumenta as chances de ocorrência de deadlocks, além de exigir que programadores sigam convenções rígidas para garantir tal tarefa.

Neste domínio, Sutter[1] e Lee[2] discutem os problemas associados e os desafios para alcançar aplicações concorrentes mais eficientes e sem gargalos. O foco está nas implicações da concorrência para os softwares e suas consequências para os desenvolvedores, linguagens e ferramentas de programação, além do estudo profundo das threads e suas alternativas. Uma das soluções propostas é a utilização de lock-free programming para permitir que múltiplas threads leiam e escrevam em dados compartilhados sem a preocupação de inconsistências. Entretanto, exige que o programador possua conhecimento profundo do modelo de memória utilizado, além de ser de difícil implementação. Sutter também considera que a programação paralela exige melhores ferramentas para sistematicamente dar suporte a descoberta de erros, debug, descoberta de gargalos e auxílio nos processos de testes das aplicações. Sem essas ferramentas, a concorrência será um fator que poderá reduzir a produtividade do desenvolvedor e do responsável pelos testes, tornando os sistemas concorrentes mais caros e de baixa qualidade. Em contrapartida, Lee enfatiza as diversas abordagens para corrigir problemas de concorrências em threads através da enorme eliminação do comportamento não determinístico, sendo um melhor processo de engenharia de software um dos fatores para suprir esta deficiência. Uma das soluções citadas é o projeto Ptolemy II Kernel, um ambiente de modelagem com suporte a modelos computacionais concorrentes. Lee também propõe outras alternativas à threads. Entre elas, destaca-se o método Rendezvous Director o qual provou-se eliminar possíveis deadlocks.

Por fim, ambos os autores concordam que o não determinismo deve ser cuidadosamente empregado e determinar onde de fato este precisa ser aplicado. Devido a esta característica intrísica das threads, acho que as soluções propostas e discutidas nos papers são de fundamental relevância no que tange alcançar eficientes técnicas de paralelismo e ferramentas de auxílio em tarefas para programação concorrente. Além disso, acredito que as tentativas das atuais linguagens de programação em incorporar técnicas para dar suporte à aplicações multithreads não conseguiram evoluir de forma plausível para acompanhar o crescimento das tecnologias de hardware.

Thiago Sales

[1] Herb Sutter and James Larus. Software and the concurrency revolution. ACM Queue, 3:54–62, 2005
[2] Edward A. Lee. The problem with threads. Computer IEEE, 39:33–42, 2005.

terça-feira, 5 de agosto de 2008

Análise e Crítica: Double-Checked Locking: Clever, but Broken

Um dos fatores primordiais em sistemas computacionais está associado a performance de uma aplicação, a qual pode ser atingida através do uso de paralelismo de tarefas. Entretanto, se não realizado um gerenciamento eficiente dos dados compartilhados entre as diferentes linhas de execução, a performance desejada pode ser significativamente afetada além de facultar o acesso a dados inconsistentes por essas tarefas. Uma das abordagens utilizadas por programadores Java é o uso da sintaxe synchronized, a qual objetiva o acesso controlado a pontos críticos entre as diferentes threads executantes. Entretanto, segundo Goetz[5], ao sincronizar um método Java através desta sintaxe, a performance de uma aplicação pode ser afetada consideravelmente em certos domínios. Isto pode ser ainda mais crítico quando esta sincronização deve ser aplicada ao padrão de projeto Singleton. A técnica Double-checked locking (DCL) possibilita "aliviar" o gargalo no acesso ao método sincronizado, provendo Lazy Inicialization (ou inicialização tardia) sem o uso de blocos sincronizados. Porém, devido a mecanismos utilizados pela JMM (Java Memory Model) para permitir melhor performance em tempo de execução, tal como a reordenação das instruções a serem executadas, a criação de uma única referência a um objeto pode ser drasticamente afetada.

Desta forma, diversos pesquisadores têm estudado e proposto algumas "artimanhas" como técnicas para evitar que o gerenciamento da JMM não efete o algorítimo desejado. Em [1], a construção do objeto a ser criado é colocado em um bloco (extra) synchronized interno. Ou seja, no momento em que a sincronização (interna) é finalizada, deve-se existir uma "barreira de memória", evitando assim que a JMM reordene as instruções para inicialização do objeto. Entretanto, ainda sim é possível que a reordenação aconteça devido a não existência de regras para impedir que a execução de instruções que estão após o bloco sincronizado seja antes de finalizar o "monitor". Uma outra solução, proposta por Goetz [3], utiliza uma variável temporária para garantir a construção total do objeto e posteriormente atribuída à referência original. Porém, ainda sim a JMM está livre para realizar otimizações nesta variável temporária. Goetz ainda propõe outra abordagem, utilizando uma variável booleana cujo valor depende diretamente da existência na memória do objeto a ser criado e posicionada fora do bloco synchronized. Porém, o compilador ou a JVM (Java Virtual Machine) pode reordenar tal instrução e adicioná-la ao bloco sincronizado para reduzir problemas associados a cache. Ou seja, o valor desta variável pode ser atribuída antes da construção do objeto. Em [4], Goetz discute a solução deste problema através da classe ThreadLocal. Em vez de verificar se o objeto possui referência nula, um objeto ThreadLocal (estático) armazena um valor (booleano, por exemplo) para garantir afirmar que uma outra thread já inicializou o objeto desejado. Por não compartilhar este valor com outras threads (pois este valor é local), problemas com reordenação de instruções não efetará a solução. Apesar de alcançar o objeto desejado pelo DCL, a performance da ThreadLocal é considerada pior que o custo existente em blocos sincronizados.

Apesar de existirem soluções plausíveis para alcançar o objetivo do Double-Checked Locking, a maior parte destas não funcionam ou possuem baixa performance/otimização. Acho que em qualquer domínio de aplicações que necessitem de alta performance sempre haverá trade-offs, ficando a cargo dos programadores decidir cuidadosamente quando um código deve ou não ser otimizado e, neste processo, se de fato haverá ganho considerável.

Ainda segundo Goetz, métodos sincronizados podem ser até 100 vezes mais lento do que métodos não-sincronizados. Para colocar em prova tal afirmação,
realizei alguns experimentos utilizando uma máquina com as seguintes configurações:

  • Processador Pentium-M, 1.86
  • 1GB Memória RAM

Utilizei um método estatístico intitulado de Distribuição Z [2]. Foram realizados 10 experimentos para cada tipo de método (sincronizado e não sincronizado), e alcancei os seguintes resultados com médias para 95% de nível de confiança:

  • Métodos sincronizados: 32071.90 milissegundos. Intervalo de confiança: entre 31743.89 e 32399.91 milissegundos.
  • Métodos não-sincronizados: 9691.00 milissegundos. Intervalo de confiança: entre 9419.32 e 9962.68 milissegundos.
Para ambos os casos, isso significa dizer que para "n" vezes que for executado esse experimento para o mesmo cenário das execuções (ou seja, com uma máquina igual ou similar as configuraçoes supracitadas), a média estará dentro do intervalo de confiança especificado. Desta forma, podemos concluir que o método sincronizado atribui maior tempo (apenas 4 vezes mais) de execução à aplicação do que o método não sincronizado.

Thiago Sales

[1] David Bacon, Joshua Bloch, Jeff Bogda, and Cliff Click. The ”double-checked locking is broken”declaration. http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html, Fevereiro 2001. Último acesso em 2 de Maio de 2008.

[2] Fisher. On a distribution yielding the error functions of several well known
statistics. Proceedings of the International Congress of Mathematics, 2:805–
813, 1924

[3] Brian Goetz. Can double-checked locking be fixed?
http://www.javaworld.com/javaworld/jw-05-2001/jw-0525-double.html, Maio 2001. Último acesso em 2 de Maio de 2008.

[4] Brian Goetz. Can threadlocal solve the double-checked locking problem?
http://www.javaworld.com/javaworld/jw-11-2001/jw-1116-dcl.html,
Novembro 2001. Último acesso em 2 de Maio de 2008.

[5] Brian Goetz. Double-checked locking: Clever, but broken.
http://www.javaworld.com/javaworld/jw-02-2001/jw-0209-double.html, Fevereiro 2001. Último acesso em 2 de Maio de 2008.

Análise e Crítica: A Pattern Language for Developing Object-Oriented Frameworks

No domínio da Orientação a Objetos (OO), um framework é um conjunto de classes que visa a reutilização de um design de software, oferecendo soluções padrões para aplicações de um mesmo domínio (framework vertical). Frameworks possibilitam também o aumento na produtividade e a diminuição no custo de desenvolvimento das aplicações. Entretanto, especificar um design para framework que ofereça também facilidade de aprendizagem tende a ser bastante complexo devido aos detalhes intrísecos no paradigma de Orientação a Objetos.

Neste contexto, [1] propõe um modelo para a implementação de frameworks. Intitulado de "Evolving Frameworks", este modelo é constituído de um conjunto de padrões que devem ser utilizados conjuntamente de forma a alcançar um design bem definido. Dentre eles, destacam-se: White-Box Framework, o qual propõe a utilização de herança ao invés de polimorfismos aliado ao uso dos padrões de projeto Template Method e Factory Method, aumentando a reusabilidade de código; Pluggable Objects onde ao observar subclasses com comportamentos semelhantes, propõe-se utilizar subclasses genéricas, possibilitando o reúso sem o conhecimento a priori do objeto; Fine-Grained Objects, o qual deve ser utilizado no estágio de refatoração do código, dividindo o objeto que possui múltiplos comportamentos em pequenos objetos com comportamentos peculiares.

Por fim, acho que a solução do paper é plausível no que tange o desenvolvimento de um bom framework, ao começar provando que, se ao menos três aplicações desenvolvidas utilizam algo em comun, a criação do framework desejado poderá ser necessária e, a partir deste ponto, desenvolver bibliotecas de componentes e refatorações de códigos. Entretanto, o paper apenas restringe a proposta a frameworks verticais, ou seja, aqueles que são dependentes do domínio da aplicação, o que torna o paper ainda mais restrito. Apesar de oferecer boas soluções, o paper mostrou-se pobre no que se refere a exemplos associados com os problemas apresentados.

[1] Don Roberts and Ralph E. Johnson. Evolving frameworks: A pattern language
for developing object-oriented frameworks. In Pattern Languages
of Program Design 3. Addison Wesley, 1997.