Phoenix is still in early development and the following benchmarks should be taken with a grain of salt and may not reflect the final version of the code. Changes may occur in the future, both positive and negative from a speed perspective. Furthermore, current benchmarks were done redumentary and not using propper benchmarking technics like using JMH.
As a starter, the template for Rocker and Thymeleaf from this repository
was used, with an additional template for Phoenix which rendered the same result. However, due
to Phoenix being in early stages of development and because it needs a Spring Context to compile
the template, a simple @SpringBootTest
was used. For Rocker, the template was
compiled before execution.
Using 100_000
iterations, the template was called and rendered. Execution time in ms
was monitored using System.currentTimeMillis()
. I know this is NOT the best
approach, but wanted to have a general idea of the performance. Propper benchmarking weill be
done in the future.
Below is a table with the results of the benchmarks on my machine (using a Ryzen 7800x3D) as well as source code of the test class that was used:
Template Engine | Execution Time |
---|---|
Phoenix | 786ms |
Rocker | 818ms |
Thymeleaf | 19251ms |
import org.example.model.Stock;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.ImportAutoConfiguration;
import org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
import org.thymeleaf.TemplateEngine;
import org.thymeleaf.context.Context;
import org.thymeleaf.context.IContext;
import tech.petrepopescu.phoenix.controllers.FragmentController;
import tech.petrepopescu.phoenix.parser.ElementFactory;
import tech.petrepopescu.phoenix.parser.PhoenixParser;
import tech.petrepopescu.phoenix.parser.compiler.Compiler;
import tech.petrepopescu.phoenix.parser.compiler.DynamicClassLoader;
import tech.petrepopescu.phoenix.parser.route.RouteGenerator;
import tech.petrepopescu.phoenix.special.PhoenixSpecialElementsUtil;
import tech.petrepopescu.phoenix.spring.PhoenixErrorHandler;
import tech.petrepopescu.phoenix.spring.PhoenixMessageConverter;
import tech.petrepopescu.phoenix.spring.SecurityConfig;
import tech.petrepopescu.phoenix.spring.config.PhoenixConfiguration;
import tech.petrepopescu.phoenix.spring.config.ViewsConfiguration;
import tech.petrepopescu.phoenix.views.View;
import java.io.StringWriter;
import java.io.Writer;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@SpringBootTest(classes = {SecurityConfig.class, PhoenixMessageConverter.class, RouteGenerator.class, Compiler.class, ElementFactory.class,
DynamicClassLoader.class, PhoenixConfiguration.class, PhoenixSpecialElementsUtil.class, FragmentController.class,
PhoenixErrorHandler.class})
@ImportAutoConfiguration(ThymeleafAutoConfiguration.class)
class FullBenchmark {
@Autowired
private Compiler compiler;
@Autowired
private RouteGenerator routeGenerator;
@Autowired
private PhoenixSpecialElementsUtil phoenixSpecialElementsUtil;
@Autowired
private TemplateEngine engine;
@Test
void benchmark() throws Exception {
List<Stock> items = Stock.dummyItems();
preparePhoenix();
IContext context = new Context(Locale.getDefault(), getThymeleafContext(items));
System.out.println("Running benchmark...");
long rocker = runBenchmark(() -> stocks.template(items).render().toString());
long phoenix = runBenchmark(() -> View.of("phoenix", items).getContent(phoenixSpecialElementsUtil));
long thymeleaf = runBenchmark(() -> {
Writer writer = new StringWriter();
engine.process("stocks.thymeleaf.html", context, writer);
writer.toString();
});
System.out.println("Phoenix Benchmark: " + phoenix + "ms");
System.out.println("Thymeleaf Benchmark: " + thymeleaf + "ms");
System.out.println("Rocker Benchmark: " + rocker + "ms");
}
private long runBenchmark(Runnable runnable) {
long start = System.currentTimeMillis();
for (int count = 0; count<100_000; count++) {
runnable.run();
}
long end = System.currentTimeMillis();
return end - start;
}
private void preparePhoenix() {
PhoenixConfiguration phoenixConfiguration = new PhoenixConfiguration();
phoenixConfiguration.setViews(new ViewsConfiguration());
phoenixConfiguration.getViews().setPath("src/test/resources/views");
PhoenixParser parser = new PhoenixParser(new ElementFactory(null), routeGenerator, compiler, phoenixConfiguration);
parser.parse();
}
protected Map<String, Object> getThymeleafContext(List<Stock> items) {
Map<String, Object> context = new HashMap<>();
context.put("items", items);
return context;
}
}