Gente
Removing Unnecessary
Synchronization in Java
Sérgio Soares
Sincronização
Métodos/blocos onde apenas um thread
executa por vez
baseado em monitores
Garantir execuções seguras
Motivação
Sincronizações desnecessárias
objetos acessíveis apenas por um thread
overhead 26-60% do tempo de execução
“remover” sincronização
Possíveis soluções
Identificar objetos acessíveis apenas por
um thread
verificação manual tediosa e erro-prone
mudanças futuras do programa forçam uma
nova análise
Definir versões não sincronizadas do
referido objeto
duplicação de código
A solução proposta
Remover em tempo de compilação a
sincronização quando a otimização for
possível
Programa de otimização (compilador)
identifica situações passíveis da remoção de
sincronização
rescreve parte do programa removendo as
sincronizações desnecessárias
Identificando situações
de otimização
Excluir os objetos que não podem ser
thread-local
objetos armazenados na heap
s-escaping (stack-escaping)
objetos acessíveis apenas por variáveis locais
na pilha de apenas um thread
s-local
s-escaping e s-local
algoritmo 1
A
B
C
A é s-local
B é s-escape
C é s-escape
Pilha do
Thread 1
Pilha do
Thread 2
Heap
s-escaping e s-local
algoritmo 2
A
B
C
A é s-local
B é s-local
C é s-escape
Pilha do
Thread 1
Pilha do
Thread 2
Heap
Algoritmo 2
s-escaping
objetos armazenados na heap ou
acessíveis por mais de uma pilha (thread)
s-local
objetos acessíveis apenas por uma única
pilha (thread)
objetos acessíveis apenas por objetos slocal do heap
Problema
Identificar objetos acessíveis apenas por
objetos s-local do heap pode levar a
algoritmos complexos
transitividade de acessibilidade
tipos de dados recursivos
Simplificação: objetos acessíveis por mais
de um nível de referência de um objeto s-
local são s-escaping
f-escaping
Objetos referenciados por atributos de
objetos s-escaping
A análise
Estágio 1
detectar objetos s-escaping
Estágio 2
detectar objetos f-escaping
Restrições da linguagem
Um subconjunto de comandos/expressões
x = y
x.f = y
y = x.f
C.f = y
y = C.f
x = new T
foo(a1, ..., an)
atribuição a variável
atribuição a atributo de objeto
acesso a atributo
atribuição a atributo de classe
acesso a atributo de classe
criação de objeto
chamada de método
usados para implementar construções de Java
como exceções, arrays, retorno de métodos
Retorno de métodos
o = hashtable.put(x, y)
put(hashtable, x, y, o)
Detectando objetos s-escaping
Define conjuntos
s-escape(x)
é vazio inicialmente (representa falso) e durante a
análise pode conter o booleano true {T},
indicando que a variável é s-escaping
AS(x)
conjunto das variáveis de um métodos que
referenciam o objeto referenciado por x
Detectando objetos s-escaping
Inicia a análise pelo método main e vai
analisando os métodos conforme
aparecem as chamadas aos mesmos
Aplicar as regras para cada tipo de
comando
atribuição
acesso a atributos
chamada de métodos
Detectando objetos s-escaping
Atribuição
unir (merge) os Alias Set e a propriedade sescape das variáveis
x = y
AS(x) AS(y)
s-escape(x) s-escape(y)
AS(x) 1 AS(y)
Análise flow-insensitive
Detectando objetos s-escaping
Acesso a atributos (incluindo estáticos)
a variável passa a ser s-escaping pois indica
que o heap tem uma referência para a
mesma ou que a mesma tem uma referência
para o heap
y = x.f ou x.f = y
s-escape(y) {T}
Detectando objetos s-escaping
Chamada de métodos
propaga as restrições impostas aos parâmetros
formais dentro do método para os parâmetros
reais (depois de analisar o método)
foo(a0, ..., an)
i [0...n] s-escape(ai) s-escape(pi)
i tal que connected(pi, returnfoo)
AS(ai) 1 AS(an)
an é o parâmetro real que recebe o
retorno do método se houver retorno
Detectando objetos f-escaping
AS(x.f)
conjunto de variáveis que referenciam o
objeto em x.f
f-escape(x)
conjunto vazio ou {T} indicando, quando
true, que o objeto referenciado por x
também é referenciado pelo atributo de um
objeto s-escaping
Detectando objetos f-escaping
Atribuição
unir (merge) os Alias Set e a propriedade fescape das variáveis e dos seus atributos
x = y
AS(x) AS(y)
f-escape(x) f-escape(y)
se s-escape(x) s-escape(y) = AS(x) 2 AS(y)
f fields(x, y)
*
AS(x.f) 2 AS(y.f)
Detectando objetos f-escaping
Acesso a atributos
x.f = y ou y = x.f
se s-escape(x) =
AS(x.f) 2 AS(y)
se não
FieldAccess(x.f, y)
f-escape(y) {T}
O que acontecerá com os Alias Set dos atributos
de x.f e de y quando aplicar 2 na recursão?
O mesmo código já
passou pela fase 1
Detectando objetos f-escaping
Acesso a atributos estáticos
C.f = y ou y = C.f
f-escape(y) {T}
Detectando objetos f-escaping
Chamada de métodos
Regra 1 - propagar a propriedade f-escape dos
parâmetros formais e seus atributos para os
parâmetros reais e seus atributos
foo(a0, ..., an)
i [0...n] f-escape(ai) f-escape(pi)
se s-escape(ai) =
i fields(pi)
f-escape(ai.f) f-escape(pi.f)
Detectando objetos f-escaping
Chamada de métodos
Regra 2 - propagar as restrições entre
parâmetros retornados e variáveis que
recebem o retorno
foo(a0, ..., an)
i tal que connected(pi, returnfoo)
AS(ai) 2 AS(an)
Detectando objetos f-escaping
Chamada de métodos
Regra 3 - propagar as restrições entre
parâmetros e atributos de outros parâmetros
onde haja atribuição entre eles (trata-se
como acesso a atributo)
foo(a0, ..., an)
f,pi,pj tal que connected(pi.f, pj)
FieldAccess (ai.f, aj)
Detectando objetos f-escaping
Chamada de métodos
Regra 4 - propagar propriedades entre atributos de
parâmetros que referenciam um mesmo objeto
foo(a0, ..., an)
f,h,pi,pj tal que connected(pi.f, pj.h)
se s-escape(ai) s-escape(aj) =
AS(ai.f) 2 AS(an.h)
se não se s-escape(ai) =
f-escape(ai.f) {T}
Exemplo
class Handle {
private Object ref = null;
synchronized void attach(Object o) {
this.ref = o;
}
}
class Sample {
static Handle globalHandle = null;
static m() {
Handle h1 = createHandle();
Handle h2 = createHandle();
Object o1 = new Object();
Object o2 = new Object();
h1.attach(o1);
h2.attach(o2);
globalHandle = h2;
}
static Handle createHandle() {
return new Handle();
}
}
Exemplo
Vamos analisar o método m da classe
Sample
Fase 1 - identificando
s-escaping
Alias Sets do
método m
Alias Set do método
createHandle
Alias Sets do método
attach
h1
h2
o2
o1
s
s
s
this
return
o
s
Fase 2 - identificando
f-escaping
ref
h1
Alias Sets do
método m
h2
o2
Alias Set do método
createHandle
Alias Sets do método
attach
o1
s
s,f
s,f
return
this
ref
o
s
Conclusão
Otimizar o uso dos objetos que não são fescape e que possuem métodos
sincronizados
otimizar h1
Otimização
Definir uma nova subclasse (clone) da
classe do objeto a ser otimizado sem
sincronizar os métodos como na classe
original (Handle)
Substituir no método analisado a
instanciação do objeto referenciado por
h1 pela nova classe (clone)
pode ser necessário duplicar factory methods
Na classe clone
Definir um construtor que invoca o
construtor da superclasse (classe a ser
otimizada)
Copiar os métodos da superclasse
removendo o qualificador synchronized
Remover qualificadores final
Alterar os qualificadores private para
protected
A classe clone
class Handle$Unsync extends Handle{
public Handle$Unsync() {
super();
}
public void attach(Object o) {
this.ref = o;
}
}
A classe e o método
otimizados
class Sample {
// ...
static m() {
Handle h1 = createHandle$Clone1();
Handle h2 = createHandle();
// ...
}
static Handle createHandle$Clone1() {
return new Handle$Unsync();
}
// ...
}
Bibliografia
Removing Unnecessary Synchronization in
Java. Jeff Bogda and Urs Holzle.
Departament of Computer Science.
University of California