diff --git a/.gitignore b/.gitignore index 93e8a09c..8b8241e0 100644 --- a/.gitignore +++ b/.gitignore @@ -63,4 +63,7 @@ results100 results5 # Rust -other-benchmarks/rust-gql/target \ No newline at end of file +other-benchmarks/rust-gql/target + +# Go +other-benchmarks/go-gql/target diff --git a/benchmarks/dotnet-graphql.js b/benchmarks/dotnet-graphql.js new file mode 100644 index 00000000..a593cb41 --- /dev/null +++ b/benchmarks/dotnet-graphql.js @@ -0,0 +1,33 @@ +"use strict"; +const { spawn } = require("child_process"); +const path = require("path"); + +const forked = spawn("dotnet", ["run", "server.cs"], { + cwd: path.join(__dirname, "..", "other-benchmarks/dotnet-gql/"), + stdio: ["ignore", "pipe", "pipe"], + shell: false, +}); + +// Handle stdout/stderr +forked.stdout.on("data", (data) => { + console.log(`stdout: ${data}`); +}); + +forked.stderr.on("data", (data) => { + console.log(`stderr: ${data}`); +}); + +forked.on("error", (error) => { + console.log(`error: ${error.message}`); +}); + +// Forward signals to the child process +process.on("SIGINT", () => { + forked.kill("SIGINT"); +}); + +process.on("SIGTERM", () => { + forked.kill("SIGTERM"); +}); + +forked.on("exit", () => console.log("dotnet-graphql exited")); diff --git a/benchmarks/go-graphql.js b/benchmarks/go-graphql.js index eea7229b..cbb9ad3a 100644 --- a/benchmarks/go-graphql.js +++ b/benchmarks/go-graphql.js @@ -1,21 +1,47 @@ "use strict"; -const { exec } = require("child_process"); +const { spawn, execSync } = require("child_process"); const path = require("path"); +const fs = require("fs"); -const forked = exec( - "go run server.go", - { cwd: path.join(__dirname, "..", "other-benchmarks/go-gql/") }, - (error, stdout, stderr) => { - if (error) { - console.log(`error: ${error.message}`); - return; - } - if (stderr) { - console.log(`stderr: ${stderr}`); - return; - } - console.log(`stdout: ${stdout}`); - }, -); -console.log(path.join(__dirname, "..", "other-benchmarks/go-gql/server.go")); -forked.on("exit", () => console.log("exited")); +const cwd = path.join(__dirname, "..", "other-benchmarks/go-gql/"); +const binaryName = process.platform === "win32" ? "go-gql.exe" : "go-gql"; +const binaryPath = path.join(cwd, "target", binaryName); + +// Build the binary +try { + console.log("Building Go binary..."); + execSync(`go build -o target/${binaryName} .`, { cwd, stdio: "inherit" }); +} catch (error) { + console.log(`Build error: ${error.message}`); + process.exit(1); +} + +const forked = spawn(binaryPath, [], { + cwd, + stdio: ["ignore", "pipe", "pipe"], + shell: false, +}); + +// Handle stdout/stderr +forked.stdout.on("data", (data) => { + console.log(`stdout: ${data}`); +}); + +forked.stderr.on("data", (data) => { + console.log(`stderr: ${data}`); +}); + +forked.on("error", (error) => { + console.log(`error: ${error.message}`); +}); + +// Forward signals to the child process +process.on("SIGINT", () => { + forked.kill("SIGINT"); +}); + +process.on("SIGTERM", () => { + forked.kill("SIGTERM"); +}); + +forked.on("exit", () => console.log("go-graphql exited")); diff --git a/other-benchmarks/dotnet-gql/server.cs b/other-benchmarks/dotnet-gql/server.cs new file mode 100644 index 00000000..0841c87c --- /dev/null +++ b/other-benchmarks/dotnet-gql/server.cs @@ -0,0 +1,135 @@ +#!/usr/bin/dotnet run +#:sdk Microsoft.NET.Sdk.Web +#:package GraphQL@8.8.2 +#:package GraphQL.SystemTextJson@8.8.2 +#:package GraphQL.Server.Transports.AspNetCore@8.3.3 +#:package GraphQL.Server.Ui.GraphiQL@8.3.3 +#:package Faker.Net@2.0.163 +#:property PublishAot=false + +using System.Security.Cryptography; +using System.Text; +using Faker; +using GraphQL; +using GraphQL.Types; + +var builder = WebApplication.CreateBuilder(args); + +// Set logging to only show warnings and errors, output to stderr +builder.Logging.ClearProviders(); +builder.Logging.AddConsole(options => +{ + options.LogToStandardErrorThreshold = LogLevel.Trace; +}); +builder.Logging.SetMinimumLevel(LogLevel.Warning); + +// Add GraphQL services +builder.Services.AddGraphQL(b => b + .AddSystemTextJson() + .AddSchema() + .AddGraphTypes(typeof(AppSchema).Assembly)); + +var app = builder.Build(); + +// GraphQL endpoint +app.UseGraphQL("/graphql"); +app.UseGraphQLGraphiQL("/"); + +Console.WriteLine("GraphQL server listening on http://localhost:4001/graphql"); +Console.WriteLine("GraphiQL playground available at http://localhost:4001/"); +app.Run("http://localhost:4001"); + +// GraphQL Types +public class BookType : ObjectGraphType +{ + public BookType() + { + Field(x => x.Id); + Field(x => x.Name); + Field(x => x.NumPages); + } +} + +public class AuthorType : ObjectGraphType +{ + public AuthorType() + { + Field(x => x.Id); + Field(x => x.Name); + Field(x => x.Company); + Field("md5", x => x.Name.ToMd5Hash()); + Field>("books") + .Resolve(context => context.Source.Books); + } +} + +public class AppQuery : ObjectGraphType +{ + public AppQuery() + { + Field>("authors") + .Resolve(context => DataGenerator.GetAuthors()); + } +} + +public class AppSchema : Schema +{ + public AppSchema(IServiceProvider serviceProvider) : base(serviceProvider) + { + Query = serviceProvider.GetRequiredService(); + } +} + +// Data Models +public record Book(string Id, string Name, int NumPages); + +public record Author(string Id, string Name, string Company, List Books); + +// MD5 Hashing Extension Method +static class Md5Extensions +{ + public static string ToMd5Hash(this string input) + { + int bytes = Encoding.UTF8.GetMaxByteCount(input.Length); + Span utf8 = bytes <= 1024 ? stackalloc byte[bytes] : new byte[bytes]; + int actualBytes = Encoding.UTF8.GetBytes(input.AsSpan(), utf8); + Span hash = stackalloc byte[16]; + MD5.HashData(utf8[..actualBytes], hash); + return Convert.ToHexString(hash); + } +} + +// Data Generator using Faker.Net +public static class DataGenerator +{ + private static readonly List _authors = GenerateAuthors(); + + public static List GetAuthors() => _authors; + + private static List GenerateAuthors() + { + var authors = new List(); + + for (int i = 0; i < 20; i++) + { + var books = new List(); + for (int k = 0; k < 3; k++) + { + books.Add(new Book( + Id: Guid.NewGuid().ToString(), + Name: Internet.DomainName(), + NumPages: RandomNumber.Next() + )); + } + + authors.Add(new Author( + Id: Guid.NewGuid().ToString(), + Name: Name.FullName(), + Company: Company.BS(), + Books: books + )); + } + + return authors; + } +}