-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
chore: basic isEnabled benchmarks #73
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -28,3 +28,5 @@ devenv.local.nix | |
|
||
# Direnv | ||
.direnv* | ||
|
||
**/BenchmarkDotNet.Artifacts/ |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
using System; | ||
using System.Text.Json; | ||
using BenchmarkDotNet.Attributes; | ||
using BenchmarkDotNet.Running; | ||
using BenchmarkDotNet.Configs; | ||
using BenchmarkDotNet.Columns; | ||
using BenchmarkDotNet.Running; | ||
using Newtonsoft.Json.Linq; | ||
using Yggdrasil; | ||
|
||
[Config(typeof(Config))] | ||
public class YggBench | ||
{ | ||
private YggdrasilEngine yggdrasilEngine; | ||
|
||
private class Config : ManualConfig | ||
{ | ||
public Config() | ||
{ | ||
AddColumn(BenchmarkDotNet.Columns.StatisticColumn.OperationsPerSecond); | ||
} | ||
} | ||
|
||
[GlobalSetup] | ||
public void Setup() | ||
{ | ||
var basePath = Path.Combine( | ||
"..", | ||
"..", | ||
"..", | ||
"..", | ||
"..", | ||
"..", | ||
"..", | ||
"..", | ||
"..", | ||
"client-specification", | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @daveleek I feel like I've downed a bottle of crazy pills here. Where the hell is this assembly actually executing!? Surely there's something better than this crazy down path? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Long time since I looked at that, but back in the day mstest would move things to run in a subfolder deep down. you could output There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't think that will work, this isn't running in a test context, its a standalone executable so I can remove test harness fluff from the benchmarks But if nothing stands out to you, it can sit, it works, it's just ugly |
||
"specifications" | ||
); | ||
var suitePath = Path.Combine(basePath, "01-simple-examples.json"); | ||
var suiteData = JObject.Parse(File.ReadAllText(suitePath)); | ||
|
||
yggdrasilEngine = new YggdrasilEngine(); | ||
yggdrasilEngine.TakeState(suiteData["state"].ToString()); | ||
} | ||
|
||
[Benchmark] | ||
public void IsFeatureAEnabled() | ||
{ | ||
yggdrasilEngine.IsEnabled("Feature.A", new Context()); | ||
} | ||
} | ||
|
||
public class Program | ||
{ | ||
public static void Main(string[] args) | ||
{ | ||
var summary = BenchmarkRunner.Run<YggBench>(); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<OutputType>Exe</OutputType> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<ImplicitUsings>enable</ImplicitUsings> | ||
<Nullable>enable</Nullable> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="BenchmarkDotNet" Version="0.13.10" /> | ||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\Yggdrasil.Engine\Yggdrasil.Engine.csproj" /> | ||
</ItemGroup> | ||
|
||
</Project> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,7 @@ | ||
plugins { | ||
// Apply the java-library plugin for API and implementation separation. | ||
id 'java-library' | ||
id "me.champeau.jmh" version "0.7.2" | ||
} | ||
|
||
repositories { | ||
|
@@ -22,6 +23,10 @@ dependencies { | |
implementation 'com.fasterxml.jackson.core:jackson-databind:2.15.1' | ||
|
||
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.14.2' | ||
|
||
jmh 'org.openjdk.jmh:jmh-core:1.33' | ||
|
||
jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.33' | ||
} | ||
|
||
// Apply a specific Java toolchain to ease working on different environments. | ||
|
@@ -35,3 +40,14 @@ tasks.named('test') { | |
// Use JUnit Platform for unit tests. | ||
useJUnitPlatform() | ||
} | ||
|
||
jmh { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The default settings for jmh spin off a ~30 minute benchmark run. I do not have the patience for that so I've fiddled so that they execute in a similar 30-60 off seconds like the others but this config could use more eyes. @gastonfournier sanity check? |
||
version = '1.33' | ||
fork = 2 | ||
includeTests = false | ||
iterations = 5 | ||
timeOnIteration = '5s' | ||
warmup = '5s' | ||
warmupForks = 1 | ||
warmupIterations = 3 | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,12 @@ | |
* Detailed information about configuring a multi-project build in Gradle can be found | ||
* in the user manual at https://docs.gradle.org/8.1/userguide/multi_project_builds.html | ||
*/ | ||
pluginManagement { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No idea why this needed to happen but Gradle flat out refused to resolve jmh without it for some reason |
||
repositories { | ||
gradlePluginPortal() | ||
mavenCentral() | ||
} | ||
} | ||
|
||
plugins { | ||
// Apply the foojay-resolver plugin to allow automatic download of JDKs | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
package io.getunleash.engine; | ||
|
||
import org.openjdk.jmh.annotations.Benchmark; | ||
import org.openjdk.jmh.annotations.Setup; | ||
import org.openjdk.jmh.annotations.State; | ||
import org.openjdk.jmh.annotations.Scope; | ||
import com.fasterxml.jackson.core.type.TypeReference; | ||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import java.nio.file.Paths; | ||
import java.io.IOException; | ||
|
||
|
||
@State(Scope.Thread) | ||
public class UnleashEngineBenchmark { | ||
|
||
private UnleashEngine engine; | ||
private final String featureFilePath = "../client-specification/specifications/01-simple-examples.json"; | ||
|
||
private static String loadFeaturesFromFile(String filePath) throws IOException { | ||
ObjectMapper mapper = new ObjectMapper(); | ||
JsonNode jsonNode = mapper.readTree(Paths.get(filePath).toFile()); | ||
JsonNode state = jsonNode.get("state"); | ||
return state.toString(); | ||
} | ||
|
||
@Setup | ||
public void setUp() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. try setting the context here and reuse it in the benchmarkFeatureToggle() method There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yep! Gave it a spin, no meaningful change to output |
||
engine = new UnleashEngine(new YggdrasilFFI("../target/release")); | ||
try { | ||
engine.takeState(loadFeaturesFromFile(featureFilePath)); | ||
} catch (Exception e) { | ||
System.out.println("Failed to setup benchmarks"); | ||
e.printStackTrace(); | ||
System.exit(1); | ||
} | ||
} | ||
|
||
@Benchmark | ||
public void benchmarkFeatureToggle() { | ||
Context context = new Context(); | ||
try { | ||
Boolean result = engine.isEnabled("Feature.A", context); | ||
} catch (Exception e) { | ||
System.out.println("Exception caught during benchmark, this is no longer a valid benchmark so early exiting"); | ||
e.printStackTrace(); | ||
System.exit(1); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -77,7 +77,6 @@ private <T> T read(Pointer pointer, TypeReference<T> typeReference) { | |
String str = pointer.getString(0, UTF_8); | ||
yggdrasil.freeResponse(pointer); | ||
try { | ||
System.out.println(str); // TODO use a logging library. SLF4J? | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeeted this for now since std in a loop generally hurts benchmark reliability |
||
return reader.forType(typeReference).readValue(str); | ||
} catch (IOException e) { | ||
throw new YggdrasilParseException(str, typeReference.getClass(), e); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -15,10 +15,18 @@ Then you can run the tests with: | |
rspec | ||
``` | ||
|
||
Benchmarks can be run with: | ||
|
||
```bash | ||
rspec scripts/benchmark.rb | ||
``` | ||
And should produce human readable output. | ||
|
||
|
||
There's also a `mem_check.rb` in the scripts folder. This is not a bullet proof test, but it can be helpful for detecting large leaks. This requires human interaction - you need to read the output and understand what it's telling you, so it's not run as part of the test suite. | ||
|
||
```bash | ||
ruby scripts/mem_check.rb | ||
rspec scripts/mem_check.rb | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just boyscouting, the previous command never worked |
||
``` | ||
|
||
## Build | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
require 'benchmark/ips' | ||
require_relative '../lib/unleash_engine' | ||
|
||
unleash_engine = UnleashEngine.new | ||
suite_path = File.join('../client-specification/specifications', '01-simple-examples.json') | ||
suite_data = JSON.parse(File.read(suite_path)) | ||
json_client_features = suite_data['state'].to_json | ||
|
||
Benchmark.ips do |x| | ||
x.report("enabled") { unleash_engine.enabled?('Feature.A', {}) } | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not strictly necessary but gives us the output we want to be able to compare against the others