diff --git a/celements-performance/pom.xml b/celements-performance/pom.xml
new file mode 100644
index 000000000..e7a83c19a
--- /dev/null
+++ b/celements-performance/pom.xml
@@ -0,0 +1,47 @@
+
+
+
+
+ com.celements
+ celements
+ 5.5
+
+ 4.0.0
+ celements-performance
+ 5.0-SNAPSHOT
+ Celements Performance metrics
+
+
+ com.celements
+ celements-model
+ 5.4
+ provided
+
+
+
+ scm:git:git@github.com:celements/celements-xwiki.git
+ scm:git:git@github.com:celements/celements-xwiki.git
+ https://github.com/celements/celements-xwiki
+ HEAD
+
+
diff --git a/celements-performance/src/main/java/com/celements/performance/BenchmarkRole.java b/celements-performance/src/main/java/com/celements/performance/BenchmarkRole.java
new file mode 100644
index 000000000..9b55bb9f5
--- /dev/null
+++ b/celements-performance/src/main/java/com/celements/performance/BenchmarkRole.java
@@ -0,0 +1,18 @@
+package com.celements.performance;
+
+import javax.validation.constraints.NotEmpty;
+import javax.validation.constraints.NotNull;
+
+import org.xwiki.component.annotation.ComponentRole;
+
+@ComponentRole
+public interface BenchmarkRole {
+
+ void bench(@NotEmpty String label);
+
+ void startBench();
+
+ @NotNull
+ String println(boolean visible);
+
+}
diff --git a/celements-performance/src/main/java/com/celements/performance/BenchmarkScriptService.java b/celements-performance/src/main/java/com/celements/performance/BenchmarkScriptService.java
new file mode 100644
index 000000000..02ae83724
--- /dev/null
+++ b/celements-performance/src/main/java/com/celements/performance/BenchmarkScriptService.java
@@ -0,0 +1,38 @@
+package com.celements.performance;
+
+import org.xwiki.component.annotation.Component;
+import org.xwiki.component.annotation.Requirement;
+import org.xwiki.script.service.ScriptService;
+
+import com.celements.model.context.ModelContext;
+
+@Component("bench")
+public class BenchmarkScriptService implements ScriptService {
+
+ @Requirement
+ private ModelContext context;
+
+ @Requirement
+ private BenchmarkRole benchSrv;
+
+ public String benchAndPrint(String label) {
+ return benchAndPrint(label, false);
+ }
+
+ public void bench(String label) {
+ benchSrv.bench(label);
+ }
+
+ public String benchAndPrint(String label, boolean visible) {
+ benchSrv.bench(label);
+ boolean requestVisible = context.getRequest().toJavaUtil()
+ .map(request -> "true".equals(request.getParameter("showBenchmark")))
+ .orElse(false);
+ return benchSrv.println(visible || requestVisible);
+ }
+
+ public void startBench() {
+ benchSrv.startBench();
+ }
+
+}
diff --git a/celements-performance/src/main/java/com/celements/performance/BenchmarkService.java b/celements-performance/src/main/java/com/celements/performance/BenchmarkService.java
new file mode 100644
index 000000000..49905ae80
--- /dev/null
+++ b/celements-performance/src/main/java/com/celements/performance/BenchmarkService.java
@@ -0,0 +1,93 @@
+package com.celements.performance;
+
+import static com.google.common.base.Preconditions.*;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.stream.Collectors;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.xwiki.component.annotation.Component;
+import org.xwiki.component.annotation.Requirement;
+import org.xwiki.context.Execution;
+
+import com.google.common.base.Strings;
+
+@Component
+public class BenchmarkService implements BenchmarkRole {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(BenchmarkService.class);
+
+ private static final String BENCH_LAST_TIME = "bench_lastTime";
+
+ private static final String BENCH_START_TIME = "bench_startTime";
+
+ private static final String CEL_BENCHMARK_VAL_PREFIX = "CEL_BENCHMARK_";
+
+ private static final String CEL_BENCHMARK_OUT_STRINGS = CEL_BENCHMARK_VAL_PREFIX
+ + "benchOutStrings";
+
+ @Requirement
+ Execution execution;
+
+ private boolean isBenchStarted() {
+ return getContextLongValue(BENCH_START_TIME) != null;
+ }
+
+ private Long getContextLongValue(String name) {
+ return (Long) execution.getContext().getProperty(CEL_BENCHMARK_VAL_PREFIX + name);
+ }
+
+ private void setContextLongValue(String name, long value) {
+ execution.getContext().setProperty(CEL_BENCHMARK_VAL_PREFIX + name, value);
+ }
+
+ @SuppressWarnings("unchecked")
+ private ArrayList getBenchOutStringArray() {
+ ArrayList benchOutStringArray;
+ if (execution.getContext().getProperty(CEL_BENCHMARK_OUT_STRINGS) == null) {
+ benchOutStringArray = new ArrayList<>();
+ execution.getContext().setProperty(CEL_BENCHMARK_OUT_STRINGS, benchOutStringArray);
+ } else {
+ benchOutStringArray = (ArrayList) execution.getContext()
+ .getProperty(CEL_BENCHMARK_OUT_STRINGS);
+ }
+ return benchOutStringArray;
+ }
+
+ @Override
+ public void bench(String label) {
+ checkArgument(!Strings.isNullOrEmpty(label));
+ if (isBenchStarted()) {
+ long currTime = new Date().getTime();
+ double totalTime = (currTime - getContextLongValue(BENCH_START_TIME)) / 1000.0;
+ double time = (currTime - getContextLongValue(BENCH_LAST_TIME)) / 1000.0;
+ setContextLongValue(BENCH_LAST_TIME, currTime);
+ getBenchOutStringArray()
+ .add("bench '" + label + "' — in " + time + "s — total " + totalTime
+ + "s");
+ } else {
+ LOGGER.info("bench called without startBench. Skipping.");
+ }
+ }
+
+ @Override
+ public void startBench() {
+ long benchStartTime = new Date().getTime();
+ setContextLongValue(BENCH_START_TIME, benchStartTime);
+ setContextLongValue(BENCH_LAST_TIME, benchStartTime);
+ }
+
+ @Override
+ public String println(boolean visible) {
+ String outStr = getBenchOutStringArray().stream()
+ .collect(Collectors.joining("\n"));
+ getBenchOutStringArray().clear();
+ if (!visible) {
+ outStr = "\n";
+ }
+ return outStr;
+ }
+
+}
diff --git a/celements-performance/src/main/resources/META-INF/MANIFEST.MF b/celements-performance/src/main/resources/META-INF/MANIFEST.MF
new file mode 100644
index 000000000..5e9495128
--- /dev/null
+++ b/celements-performance/src/main/resources/META-INF/MANIFEST.MF
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Class-Path:
+
diff --git a/celements-performance/src/main/resources/META-INF/components.txt b/celements-performance/src/main/resources/META-INF/components.txt
new file mode 100644
index 000000000..1a12d83ca
--- /dev/null
+++ b/celements-performance/src/main/resources/META-INF/components.txt
@@ -0,0 +1,2 @@
+com.celements.performance.BenchmarkService
+com.celements.performance.BenchmarkScriptService