In letzter Zeit sind in der Java-Welt viele interessante Dinge passiert. Eines dieser Ereignisse war die Veröffentlichung der ersten produktionsfertigen Version von Graal VM.
Persönlich war Graal für mich seit langem eine Quelle des unverhüllten Interesses, und ich verfolge die Berichte und die neuesten Nachrichten in diesem Bereich genau. Einmal sah ich einen Bericht von Chris Talinger. Darin spricht Chris darüber, wie Twitter durch die Verwendung von Algorithmen für maschinelles Lernen zur Optimierung von Graal signifikante Leistungssteigerungen erzielt hat. Ich hatte das starke Verlangen, so etwas selbst auszuprobieren. In diesem Artikel möchte ich mitteilen, was am Ende passiert ist.
Experiment
Um das Experiment durchzuführen, brauchte ich:
- frische Graal VM Community Edition. Zum Zeitpunkt dieses Schreibens ist es 20.2.0
- dedizierte Cloud-Umgebung für Lasttests
- NewRelic zum Sammeln von Metriken
- Lastgenerator testen
- ein Python-Programm und eine Reihe von Skripten zur Implementierung des ML-Algorithmus
,
:
, .
, :
-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.