From 84a7fe30c8c2202be82fc7e17f05f183c7dcdcd4 Mon Sep 17 00:00:00 2001 From: kezhenxu94 Date: Tue, 29 Jun 2021 17:46:42 +0800 Subject: [PATCH] Perf: cache regex pattern and result, optimize string concatenation (#7199) The possible metrics names are relatively fixed in an OAP run, we previously always escape the metrics names, building a new regex Pattern from scratch, which is cpu-consuming. In this patch, I cache the escaped metrics names, and if it missed, use a pre-compiled regex Pattern to escape the metrics name. This patch also replace string concatenation `+` with `StringBuilder` in generated classes, which won't get optimized by Java compiler This may reduce 1~1.5 vCPU under 20K RPS environment. --- CHANGES.md | 1 + .../prometheus/PrometheusMetricConverter.java | 31 ++++++++++++++++--- .../resources/code-templates/metrics/id.ftl | 13 +++----- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index f8abf95d77..198cea829b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -47,6 +47,7 @@ Release Notes. * Performance: optimize Envoy ALS analyzer performance in high traffic load scenario (reduce ~1cpu in ~10k RPS). * Performance: trim useless metadata fields in Envoy ALS metadata to improve performance. * Fix: slowDBAccessThreshold dynamic config error when not configured. +* Performance: cache regex pattern and result, optimize string concatenation in Envy ALS analyzer. #### UI * Fix the date component for log conditions. diff --git a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/prometheus/PrometheusMetricConverter.java b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/prometheus/PrometheusMetricConverter.java index 9af3960717..54a40d6211 100644 --- a/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/prometheus/PrometheusMetricConverter.java +++ b/oap-server/analyzer/meter-analyzer/src/main/java/org/apache/skywalking/oap/meter/analyzer/prometheus/PrometheusMetricConverter.java @@ -18,9 +18,17 @@ package org.apache.skywalking.oap.meter.analyzer.prometheus; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.CacheLoader; +import com.google.common.cache.LoadingCache; import com.google.common.collect.ImmutableMap; import io.vavr.Tuple; import io.vavr.Tuple2; +import java.util.Collections; +import java.util.Optional; +import java.util.concurrent.ExecutionException; +import java.util.regex.Pattern; +import java.util.stream.Stream; import lombok.extern.slf4j.Slf4j; import org.apache.skywalking.oap.meter.analyzer.MetricConvert; import org.apache.skywalking.oap.meter.analyzer.dsl.Sample; @@ -34,10 +42,6 @@ import org.apache.skywalking.oap.server.library.util.prometheus.metrics.Histogra import org.apache.skywalking.oap.server.library.util.prometheus.metrics.Metric; import org.apache.skywalking.oap.server.library.util.prometheus.metrics.Summary; -import java.util.Collections; -import java.util.Optional; -import java.util.stream.Stream; - import static com.google.common.collect.ImmutableMap.toImmutableMap; import static io.vavr.API.$; import static io.vavr.API.Case; @@ -51,11 +55,23 @@ import static org.apache.skywalking.oap.meter.analyzer.Analyzer.NIL; */ @Slf4j public class PrometheusMetricConverter { + private final Pattern metricsNameEscapePattern; + + private final LoadingCache escapedMetricsNameCache = + CacheBuilder.newBuilder() + .maximumSize(1000) + .build(new CacheLoader() { + @Override + public String load(final String name) { + return metricsNameEscapePattern.matcher(name).replaceAll("_"); + } + }); private final MetricConvert convert; public PrometheusMetricConverter(Rule rule, MeterSystem service) { this.convert = new MetricConvert(rule, service); + this.metricsNameEscapePattern = Pattern.compile("\\."); } /** @@ -146,6 +162,11 @@ public class PrometheusMetricConverter { // Returns the escaped name of the given one, with "." replaced by "_" protected String escapedName(final String name) { - return name.replaceAll("\\.", "_"); + try { + return escapedMetricsNameCache.get(name); + } catch (ExecutionException e) { + log.error("Failed to get escaped metrics name from cache", e); + return metricsNameEscapePattern.matcher(name).replaceAll("_"); + } } } diff --git a/oap-server/oal-rt/src/main/resources/code-templates/metrics/id.ftl b/oap-server/oal-rt/src/main/resources/code-templates/metrics/id.ftl index 8f47e72367..141b0c862e 100644 --- a/oap-server/oal-rt/src/main/resources/code-templates/metrics/id.ftl +++ b/oap-server/oal-rt/src/main/resources/code-templates/metrics/id.ftl @@ -1,13 +1,10 @@ public String id() { -String splitJointId = String.valueOf(getTimeBucket()); +StringBuilder splitJointId = new StringBuilder(String.valueOf(getTimeBucket())); <#list fieldsFromSource as sourceField> <#if sourceField.isID()> - <#if sourceField.getTypeName() == "java.lang.String"> - splitJointId += org.apache.skywalking.oap.server.core.Const.ID_CONNECTOR + ${sourceField.fieldName}; - <#else> - splitJointId += org.apache.skywalking.oap.server.core.Const.ID_CONNECTOR + String.valueOf(${sourceField.fieldName}); - + splitJointId.append(org.apache.skywalking.oap.server.core.Const.ID_CONNECTOR) + .append(${sourceField.fieldName}); -return splitJointId; -} \ No newline at end of file +return splitJointId.toString(); +} -- GitLab