Beaucoup de choses intéressantes se sont produites dans le monde Java ces derniers temps. L'un de ces événements a été la sortie de la première version prête pour la production de Graal VM.
Personnellement, Graal a longtemps été une source d'intérêt non dissimulé pour moi, et je suis de près les rapports et les dernières nouvelles dans ce domaine. À un moment donné, j'ai vu un rapport de Chris Talinger. Dans ce document, Chris explique comment Twitter a réussi à obtenir des gains de performances significatifs en utilisant des algorithmes d'apprentissage automatique pour régler Graal. J'avais un fort désir d'essayer quelque chose comme ça moi-même. Dans cet article, je veux partager ce qui s'est passé à la fin.
Expérience
Pour mettre en œuvre l'expérience, j'avais besoin de:
- nouvelle édition communautaire Graal VM. Au moment d'écrire ces lignes, il est 20.2.0
- environnement cloud dédié pour les tests de charge
- NewRelic pour la collecte de métriques
- générateur de charge d'essai
- un programme Python et un ensemble de scripts pour implémenter l'algorithme ML lui-même
,
:
, .
, :
-Dgraal.MaximumInliningSize -Dgraal.TrivialInliningSize -Dgraal.SmallCompiledLowLevelGraphSize
. ,
.
.
:
- JVM
.
Twitter . .
,
. , .
NewRelic
NewRelic REST API APP_ID API_KEY.
APP_ID — . APM.
API_KEY NewRelic.
:
{
"metric_data": {
"from": "time",
"to": "time",
"metrics_not_found": "string",
"metrics_found": "string",
"metrics": [
{
"name": "string",
"timeslices": [
{
"from": "time",
"to": "time",
"values": "hash"
}
]
}
]
}
}
:
def request_metrics(params):
app_id = "APP_ID"
url = "https://api.newrelic.com/v2/applications/"+ app_id + "/metrics/data.json"
headers = {'X-Api-Key':"API_KEY"}
response = requests.get(url, headers=headers, params=params)
return response.json()
CPU Utilzation params :
params = {
'names[]': "CPU/User/Utilization",
'values[]': "percent",
'from': timerange[0],
'to': timerange[1],
'raw': "false"
}
timerange .
def get_timeslices(response_json, value_name):
metrics = response_json['metric_data']['metrics'][0]
timeslices = metrics['timeslices']
values = []
for t in timeslices:
values.append(t['values'][value_name])
return values
— .
.
def objective_function(maximumInliningSize, trivialInliningSize, smallCompiledLowLevelGraphSize):
update_config(int(maximumInliningSize), int(trivialInliningSize), int(smallCompiledLowLevelGraphSize))
timerange = do_test()
data = get_results(timerange)
return calculate(data)
_updateconfig , . _dotest .
JVM . , .
calculate :
value = 1 / (mean(filtered_data))
pbounds = {
'maximumInliningSize': (200, 500),
'trivialInliningSize': (10, 25),
'smallCompiledLowLevelGraphSize': (200, 650)
}
,
optimizer.probe(
params={"maximumInliningSize": 300.0,
"trivialInliningSize": 10.0,
"smallCompiledLowLevelGraphSize": 300.0},
lazy=True,
)
def autotune():
pbounds = {
'maximumInliningSize': (200, 500),
'trivialInliningSize': (10, 25),
'smallCompiledLowLevelGraphSize': (200, 650)
}
optimizer = BayesianOptimization(
f=objective_function,
pbounds=pbounds,
random_state=1,
)
optimizer.probe(
params={"maximumInliningSize": 300.0,
"trivialInliningSize": 10.0,
"smallCompiledLowLevelGraphSize": 300.0},
lazy=True,
)
optimizer.maximize(
init_points=2,
n_iter=10,
)
print(optimizer.max)
_objectivefunction 12
, . , .
:
for i, res in enumerate(optimizer.res):
print("Iteration {}: \n\t{}".format(i, res))
.
Iteration 0:
{'target': 0.02612330198537095, 'params': {'maximumInliningSize': 300.0, 'smallCompiledLowLevelGraphSize': 300.0, 'trivialInliningSize': 10.0}}
Iteration 1:
{'target': 0.02666666666666667, 'params': {'maximumInliningSize': 325.1066014107722, 'smallCompiledLowLevelGraphSize': 524.1460220489712, 'trivialInliningSize': 10.001715622260173}}
...
.
, CPU Utilization Graal
:
.
CPU 4-5%.
CPU , proof of concept
.
2 Java Graal 2 . Graal JVM , Scala Kotlin.