Skip to content

Commit d670af7

Browse files
authored
perf: Cache jstrings during metrics collection (#1029)
* Attempt at caching Jstrings as GlobalRefs in a HashMap to reduce reallocations. I need to confirm 1) there's actually a performance benefit to this, and 2) these GlobalRefs are being released when I want them to be. * Minor refactor and added more docs. * Undo import reordering to reduce diff. * Docs. * Avoid get() by just cloning the Arc to globalref on insert. * Store jstring cache in ExecutionContext.
1 parent 0e9349f commit d670af7

File tree

2 files changed

+31
-5
lines changed

2 files changed

+31
-5
lines changed

native/core/src/execution/jni_api.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ struct ExecutionContext {
8787
pub debug_native: bool,
8888
/// Whether to write native plans with metrics to stdout
8989
pub explain_native: bool,
90+
/// Map of metrics name -> jstring object to cache jni_NewStringUTF calls.
91+
pub metrics_jstrings: HashMap<String, Arc<GlobalRef>>,
9092
}
9193

9294
/// Accept serialized query plan and return the address of the native query plan.
@@ -178,6 +180,7 @@ pub unsafe extern "system" fn Java_org_apache_comet_Native_createPlan(
178180
session_ctx: Arc::new(session),
179181
debug_native,
180182
explain_native,
183+
metrics_jstrings: HashMap::new(),
181184
});
182185

183186
Ok(Box::into_raw(exec_context) as i64)
@@ -441,10 +444,11 @@ pub extern "system" fn Java_org_apache_comet_Native_releasePlan(
441444
}
442445

443446
/// Updates the metrics of the query plan.
444-
fn update_metrics(env: &mut JNIEnv, exec_context: &ExecutionContext) -> CometResult<()> {
447+
fn update_metrics(env: &mut JNIEnv, exec_context: &mut ExecutionContext) -> CometResult<()> {
445448
let native_query = exec_context.root_op.as_ref().unwrap();
446449
let metrics = exec_context.metrics.as_obj();
447-
update_comet_metric(env, metrics, native_query)
450+
let metrics_jstrings = &mut exec_context.metrics_jstrings;
451+
update_comet_metric(env, metrics, native_query, metrics_jstrings)
448452
}
449453

450454
fn convert_datatype_arrays(

native/core/src/execution/metrics/utils.rs

+25-3
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,15 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18+
use crate::jvm_bridge::jni_new_global_ref;
1819
use crate::{
1920
errors::CometError,
2021
jvm_bridge::{jni_call, jni_new_string},
2122
};
2223
use datafusion::physical_plan::ExecutionPlan;
24+
use jni::objects::{GlobalRef, JString};
2325
use jni::{objects::JObject, JNIEnv};
26+
use std::collections::HashMap;
2427
use std::sync::Arc;
2528

2629
/// Updates the metrics of a CometMetricNode. This function is called recursively to
@@ -30,6 +33,7 @@ pub fn update_comet_metric(
3033
env: &mut JNIEnv,
3134
metric_node: &JObject,
3235
execution_plan: &Arc<dyn ExecutionPlan>,
36+
metrics_jstrings: &mut HashMap<String, Arc<GlobalRef>>,
3337
) -> Result<(), CometError> {
3438
update_metrics(
3539
env,
@@ -41,6 +45,7 @@ pub fn update_comet_metric(
4145
.map(|m| m.value())
4246
.map(|m| (m.name(), m.as_usize() as i64))
4347
.collect::<Vec<_>>(),
48+
metrics_jstrings,
4449
)?;
4550

4651
unsafe {
@@ -51,7 +56,7 @@ pub fn update_comet_metric(
5156
if child_metric_node.is_null() {
5257
continue;
5358
}
54-
update_comet_metric(env, &child_metric_node, child_plan)?;
59+
update_comet_metric(env, &child_metric_node, child_plan, metrics_jstrings)?;
5560
}
5661
}
5762
Ok(())
@@ -62,11 +67,28 @@ fn update_metrics(
6267
env: &mut JNIEnv,
6368
metric_node: &JObject,
6469
metric_values: &[(&str, i64)],
70+
metrics_jstrings: &mut HashMap<String, Arc<GlobalRef>>,
6571
) -> Result<(), CometError> {
6672
unsafe {
6773
for &(name, value) in metric_values {
68-
let jname = jni_new_string!(env, &name)?;
69-
jni_call!(env, comet_metric_node(metric_node).set(&jname, value) -> ())?;
74+
// Perform a lookup in the jstrings cache.
75+
if let Some(map_global_ref) = metrics_jstrings.get(name) {
76+
// Cache hit. Extract the jstring from the global ref.
77+
let jobject = map_global_ref.as_obj();
78+
let jstring = JString::from_raw(**jobject);
79+
// Update the metrics using the jstring as a key.
80+
jni_call!(env, comet_metric_node(metric_node).set(&jstring, value) -> ())?;
81+
} else {
82+
// Cache miss. Allocate a new string, promote to global ref, and insert into cache.
83+
let local_jstring = jni_new_string!(env, &name)?;
84+
let global_ref = jni_new_global_ref!(env, local_jstring)?;
85+
let arc_global_ref = Arc::new(global_ref);
86+
metrics_jstrings.insert(name.to_string(), Arc::clone(&arc_global_ref));
87+
let jobject = arc_global_ref.as_obj();
88+
let jstring = JString::from_raw(**jobject);
89+
// Update the metrics using the jstring as a key.
90+
jni_call!(env, comet_metric_node(metric_node).set(&jstring, value) -> ())?;
91+
}
7092
}
7193
}
7294
Ok(())

0 commit comments

Comments
 (0)