From fa28dac3a705ad701c5f24f1715f8708266cddbe Mon Sep 17 00:00:00 2001 From: Tedd Hansen Date: Sat, 22 Oct 2022 23:25:02 +0200 Subject: [PATCH 1/6] Benchmark and improvements --- Trannet.Benchmark/.gitignore | 398 ++++++++++++++++++ .../Benchmarks/TrannetBenchmarks.cs | 245 +++++++++++ Trannet.Benchmark/Program.cs | 15 + Trannet.Benchmark/README.md | 61 +++ Trannet.Benchmark/Trannet.Benchmark.csproj | 22 + .../_01_Original/GTFSService.cs | 170 ++++++++ .../_02_ListAndDictionaryUse/GTFS.cs | 76 ++++ .../_02_ListAndDictionaryUse/GTFSService.cs | 38 ++ .../_02_ListAndDictionaryUse/Models.cs | 56 +++ .../TrannetVersions/_03_CacheFriendly/GTFS.cs | 110 +++++ .../_03_CacheFriendly/GTFSService.cs | 38 ++ .../_03_CacheFriendly/Models.cs | 56 +++ .../TrannetVersions/_11_StructFix/GTFS.cs | 110 +++++ .../_11_StructFix/GTFSService.cs | 43 ++ .../TrannetVersions/_11_StructFix/Models.cs | 59 +++ Trannet/.gitignore | 398 ++++++++++++++++++ Trannet/Program.cs | 18 +- Trannet/Trannet.csproj | 3 +- Trannet/Trannet.sln | 31 ++ Trannet/obj/Trannet.csproj.nuget.dgspec.json | 60 ++- Trannet/obj/Trannet.csproj.nuget.g.props | 29 +- Trannet/obj/Trannet.csproj.nuget.g.targets | 10 +- Trannet/obj/project.assets.json | 151 ++++++- Trannet/obj/project.nuget.cache | 16 +- 24 files changed, 2167 insertions(+), 46 deletions(-) create mode 100644 Trannet.Benchmark/.gitignore create mode 100644 Trannet.Benchmark/Benchmarks/TrannetBenchmarks.cs create mode 100644 Trannet.Benchmark/Program.cs create mode 100644 Trannet.Benchmark/README.md create mode 100644 Trannet.Benchmark/Trannet.Benchmark.csproj create mode 100644 Trannet.Benchmark/TrannetVersions/_01_Original/GTFSService.cs create mode 100644 Trannet.Benchmark/TrannetVersions/_02_ListAndDictionaryUse/GTFS.cs create mode 100644 Trannet.Benchmark/TrannetVersions/_02_ListAndDictionaryUse/GTFSService.cs create mode 100644 Trannet.Benchmark/TrannetVersions/_02_ListAndDictionaryUse/Models.cs create mode 100644 Trannet.Benchmark/TrannetVersions/_03_CacheFriendly/GTFS.cs create mode 100644 Trannet.Benchmark/TrannetVersions/_03_CacheFriendly/GTFSService.cs create mode 100644 Trannet.Benchmark/TrannetVersions/_03_CacheFriendly/Models.cs create mode 100644 Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFS.cs create mode 100644 Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFSService.cs create mode 100644 Trannet.Benchmark/TrannetVersions/_11_StructFix/Models.cs create mode 100644 Trannet/.gitignore create mode 100644 Trannet/Trannet.sln diff --git a/Trannet.Benchmark/.gitignore b/Trannet.Benchmark/.gitignore new file mode 100644 index 0000000..8a30d25 --- /dev/null +++ b/Trannet.Benchmark/.gitignore @@ -0,0 +1,398 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml diff --git a/Trannet.Benchmark/Benchmarks/TrannetBenchmarks.cs b/Trannet.Benchmark/Benchmarks/TrannetBenchmarks.cs new file mode 100644 index 0000000..7888e85 --- /dev/null +++ b/Trannet.Benchmark/Benchmarks/TrannetBenchmarks.cs @@ -0,0 +1,245 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Xml; + +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Diagnosers; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Loggers; + +using CommandLine; + +namespace Trannet.Benchmark.Benchmarks; + +static class JobExtensions +{ + +} + +class BenchmarkConfig : ManualConfig +{ + public void AddRuntimes() + { + AddDefaults(AddJob(Job.Default + .WithPlatform(Platform.X64) + .WithJit(Jit.RyuJit) + .WithRuntime(CoreRuntime.Core60) + .WithId("Net60"))); + + AddDefaults(AddJob(Job.Default + .WithPlatform(Platform.X64) + .WithJit(Jit.RyuJit) + .WithRuntime(CoreRuntime.Core70) + .WithId("Net70"))); + + + //Column.Runtime, Column.Platform, Column.Baseline, Column.Rank, Column.Ratio, Column.Min, Column.Max, Column.Mean, Column.Median, Column.StdErr, Column.StdDev, Column.OperationPerSecond, Column.Rank, Column.Allocated, Column.AllocRatio, Column.AllocatedNativeMemory, Column.Gen0, Column.Gen1, Column.Gen2 + + } + public ManualConfig AddDefaults(ManualConfig config) => config + .AddDiagnoser(MemoryDiagnoser.Default) + .AddExporter(MarkdownExporter.GitHub, HtmlExporter.Default, AsciiDocExporter.Default, RPlotExporter.Default) + .AddLogger(ConsoleLogger.Default) + .AddColumn(JobCharacteristicColumn.AllColumns) + .AddColumn(StatisticColumn.AllStatistics) + .AddColumn(RankColumn.Arabic); + + public BenchmarkConfig() + { + AddRuntimes(); + } +} + +[RankColumn, BaselineColumn] +[Config(typeof(BenchmarkConfig))] +public class LoadTripsBenchmarks +{ + [Benchmark(Baseline = true)] + public void _01_Original() + { + TrannetVersions._01_Original.GTFS.LoadTrips(); + } + + [Benchmark()] + public void _02_ListAndDictionaryUse() + { + TrannetVersions._02_ListAndDictionaryUse.GTFS.LoadTrips(); + } + + [Benchmark()] + public void _03_CacheFriendly() + { + TrannetVersions._03_CacheFriendly.GTFS.LoadTrips(); + } +} + +[Config(typeof(BenchmarkConfig))] +[RankColumn, BaselineColumn] +public class LoadStopTimesBenchmarks +{ + [Benchmark(Baseline = true)] + public void _01_Original() + { + TrannetVersions._01_Original.GTFS.LoadStopTimes(); + } + + [Benchmark()] + public void _02_ListAndDictionaryUse() + { + TrannetVersions._02_ListAndDictionaryUse.GTFS.LoadStopTimes(); + } + + [Benchmark()] + public void _03_CacheFriendly() + { + TrannetVersions._03_CacheFriendly.GTFS.LoadStopTimes(); + } +} + +[MemoryDiagnoser] +[MarkdownExporter] +[MinColumn, MaxColumn, MeanColumn, MedianColumn] +[Config(typeof(BenchmarkConfig))] +public class SchedulesForRouteBenchmarks +{ + string[] routes = { + "Mattapan", + "Orange", + "Green-B", + "Green-C", + "Green-D", + "Green-E", + "Blue", + "741", + "742", + "743", + "751", + "749", + "746", + "CR-Fairmount", + "CR-Fitchburg", + "CR-Worcester", + "CR-Franklin", + "CR-Greenbush", + "CR-Haverhill", + "CR-Kingston", + "CR-Lowell", + "CR-Middleborough", + "CR-Needham", + "CR-Newburyport", + "CR-Providence", + "CR-Foxboro", + "Boat-F4", + "Boat-F1", + "Boat-EastBoston", + "747", + "708", + "1", + "4", + "7", + "8", + "9", + "10", + "11", + "14", + "15", + "16", + "17", + "18", + "19", + "21", + "22", + "23", + "24", + "26", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "34E", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "43", + "44", + "45", + "47", + "50", + "51", + "52", + "55", + "57", + "59", + "60", + "61", + "62", + "627", + "64", + "65", + "66", + "67", + "68", + "69", + "70", + "71", + "72", + "73", + "74", + "75", + "76", + "77", + "78", + "79", + "80", + "83", + "84", + "85", +}; + + + [GlobalSetup] + public void GlobalSetup() + { + // We don't want initial load to be part of test + _ = TrannetVersions._01_Original.GTFSService.SchedulesForRoute("0"); + _ = TrannetVersions._02_ListAndDictionaryUse.GTFSService.SchedulesForRoute("0"); + _ = TrannetVersions._03_CacheFriendly.GTFSService.SchedulesForRoute("0"); + + // Multiple datasets with some churn, lets clean up before test + GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced, blocking: true, compacting: true); + + // Shuffle string array + var rnd = new Random(); + routes = routes.OrderBy(x => rnd.Next()).ToArray(); + } + + [Benchmark(Baseline = true)] + public void _01_Original() + { + for (int i = 0; i < routes.Length; i++) + _ = TrannetVersions._01_Original.GTFSService.SchedulesForRoute(routes[i]); + } + + [Benchmark()] + public void _11_StructFix() + { + for (int i = 0; i < routes.Length; i++) + _ = TrannetVersions._11_StructFix.GTFSService.SchedulesForRoute(routes[i]); + } + +} diff --git a/Trannet.Benchmark/Program.cs b/Trannet.Benchmark/Program.cs new file mode 100644 index 0000000..0780fc6 --- /dev/null +++ b/Trannet.Benchmark/Program.cs @@ -0,0 +1,15 @@ +using BenchmarkDotNet.Columns; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Environments; +using BenchmarkDotNet.Exporters; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Running; +using Microsoft.Diagnostics.Tracing.Analysis; +using Trannet.Benchmark.Benchmarks; + +var summary1 = BenchmarkRunner.Run(); +var summary2 = BenchmarkRunner.Run(); +var summary3 = BenchmarkRunner.Run(); + + diff --git a/Trannet.Benchmark/README.md b/Trannet.Benchmark/README.md new file mode 100644 index 0000000..7223629 --- /dev/null +++ b/Trannet.Benchmark/README.md @@ -0,0 +1,61 @@ +# Improvements on C# sample + +``` ini + +BenchmarkDotNet=v0.13.2, OS=Windows 11 (10.0.22000.1098/21H2) +AMD Ryzen 9 5950X, 1 CPU, 32 logical and 16 physical cores +.NET SDK=7.0.100-rc.1.22431.12 + [Host] : .NET 7.0.0 (7.0.22.42610), X64 RyuJIT AVX2 [AttachedDebugger] + Net70 : .NET 7.0.0 (7.0.22.42610), X64 RyuJIT AVX2 + +Jit=RyuJit Platform=X64 + +``` + +## LoadTrips + +| Method | Job | Runtime | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Ratio | RatioSD | Rank | Baseline | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio | +| ------------------------ | ----- | -------- | -------: | -------: | -------: | -------: | -------: | -------: | -------: | -------: | -------: | ----: | ----: | ------: | ---: | -------- | --------: | --------: | --------: | ---------: | ----------: | +| _01_Original | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | Yes | - | - | - | - | ? | +| _02_ListAndDictionaryUse | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | No | - | - | - | - | ? | +| _03_CacheFriendly | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | No | - | - | - | - | ? | +| | | | | | | | | | | | | | | | | | | | | | | +| _01_Original | Net70 | .NET 7.0 | 72.36 ms | 1.394 ms | 1.763 ms | 0.368 ms | 68.69 ms | 71.32 ms | 72.89 ms | 73.93 ms | 74.63 ms | 13.82 | 1.00 | 0.00 | 3 | Yes | 4285.7143 | 3142.8571 | 1285.7143 | 63080637 B | 1.00 | +| _02_ListAndDictionaryUse | Net70 | .NET 7.0 | 58.80 ms | 1.138 ms | 1.633 ms | 0.309 ms | 55.40 ms | 57.41 ms | 59.06 ms | 59.67 ms | 61.51 ms | 17.01 | 0.81 | 0.03 | 2 | No | 3500.0000 | 3375.0000 | 1125.0000 | 47521557 B | 0.75 | +| _03_CacheFriendly | Net70 | .NET 7.0 | 46.91 ms | 0.855 ms | 1.331 ms | 0.235 ms | 44.42 ms | 45.92 ms | 46.64 ms | 47.63 ms | 49.94 ms | 21.32 | 0.65 | 0.03 | 1 | No | 3272.7273 | 2454.5455 | 909.0909 | 46362841 B | 0.73 | + +Benchmarks with issues: + LoadTripsBenchmarks._01_Original: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) + LoadTripsBenchmarks._02_ListAndDictionaryUse: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) + LoadTripsBenchmarks._03_CacheFriendly: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) + +## LoadStop + +| Method | Job | Runtime | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Ratio | RatioSD | Rank | Baseline | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio | +| ------------------------ | ----- | -------- | ------: | -------: | -------: | -------: | ------: | ------: | ------: | ------: | ------: | -----: | ----: | ------: | ---: | -------- | ---------: | ---------: | --------: | -----------: | ----------: | +| _01_Original | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | Yes | - | - | - | - | ? | +| _02_ListAndDictionaryUse | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | No | - | - | - | - | ? | +| _03_CacheFriendly | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | No | - | - | - | - | ? | +| | | | | | | | | | | | | | | | | | | | | | | +| _01_Original | Net70 | .NET 7.0 | 1.477 s | 0.0267 s | 0.0237 s | 0.0063 s | 1.425 s | 1.469 s | 1.478 s | 1.489 s | 1.518 s | 0.6769 | 1.00 | 0.00 | 3 | Yes | 62000.0000 | 61000.0000 | 5000.0000 | 1157427448 B | 1.00 | +| _02_ListAndDictionaryUse | Net70 | .NET 7.0 | 1.250 s | 0.0190 s | 0.0159 s | 0.0044 s | 1.222 s | 1.240 s | 1.252 s | 1.265 s | 1.270 s | 0.8001 | 0.85 | 0.02 | 2 | No | 50000.0000 | 49000.0000 | 6000.0000 | 869224072 B | 0.75 | +| _03_CacheFriendly | Net70 | .NET 7.0 | 1.168 s | 0.0107 s | 0.0095 s | 0.0025 s | 1.156 s | 1.161 s | 1.164 s | 1.172 s | 1.186 s | 0.8562 | 0.79 | 0.02 | 1 | No | 51000.0000 | 50000.0000 | 6000.0000 | 899098344 B | 0.78 | + +Benchmarks with issues: + LoadStopTimesBenchmarks._01_Original: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) + LoadStopTimesBenchmarks._02_ListAndDictionaryUse: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) + LoadStopTimesBenchmarks._03_CacheFriendly: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) + +## SchedulesForRoute + +| Method | Job | Runtime | Mean | Error | StdDev | StdErr | Min | Max | Median | Q1 | Q3 | Op/s | Ratio | RatioSD | Rank | Gen0 | Gen1 | Allocated | Alloc Ratio | +| ------------- | ----- | -------- | -------: | -------: | -------: | -------: | -------: | -------: | -------: | -------: | -------: | ----: | ----: | ------: | ---: | --------: | --------: | ---------: | ----------: | +| _01_Original | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | - | - | - | ? | +| _11_StructFix | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | - | - | - | ? | +| | | | | | | | | | | | | | | | | | | | | +| _01_Original | Net70 | .NET 7.0 | 36.88 ms | 0.419 ms | 0.392 ms | 0.101 ms | 36.31 ms | 37.59 ms | 36.72 ms | 36.61 ms | 37.27 ms | 27.11 | 1.00 | 0.00 | 2 | 5000.0000 | 1214.2857 | 85135720 B | 1.00 | +| _11_StructFix | Net70 | .NET 7.0 | 23.20 ms | 0.445 ms | 0.437 ms | 0.109 ms | 22.73 ms | 24.12 ms | 23.09 ms | 22.85 ms | 23.38 ms | 43.11 | 0.63 | 0.01 | 1 | 2437.5000 | 875.0000 | 40924442 B | 0.48 | + +Benchmarks with issues: + SchedulesForRouteBenchmarks._01_Original: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) + SchedulesForRouteBenchmarks._11_StructFix: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) diff --git a/Trannet.Benchmark/Trannet.Benchmark.csproj b/Trannet.Benchmark/Trannet.Benchmark.csproj new file mode 100644 index 0000000..b52da1b --- /dev/null +++ b/Trannet.Benchmark/Trannet.Benchmark.csproj @@ -0,0 +1,22 @@ + + + + Exe + net7.0 + enable + enable + + + + + + + + + PreserveNewest + True + + + + + diff --git a/Trannet.Benchmark/TrannetVersions/_01_Original/GTFSService.cs b/Trannet.Benchmark/TrannetVersions/_01_Original/GTFSService.cs new file mode 100644 index 0000000..45a756a --- /dev/null +++ b/Trannet.Benchmark/TrannetVersions/_01_Original/GTFSService.cs @@ -0,0 +1,170 @@ +using System.Diagnostics; +using BenchmarkDotNet.Helpers; + +namespace Trannet.Benchmark.TrannetVersions._01_Original; + +public static class GTFSService +{ + static List Trips { get; } + static Dictionary> TripsIxByRoute { get; } + static List StopTimes { get; } + static Dictionary> StopTimesIxByTrip { get; } + + static GTFSService() + { + (Trips, TripsIxByRoute) = GTFS.LoadTrips(); + (StopTimes, StopTimesIxByTrip) = GTFS.LoadStopTimes(); + } + + public static List SchedulesForRoute(string route) + { + var trips = new List(); + if (TripsIxByRoute.ContainsKey(route)) + { + var tripIxs = TripsIxByRoute[route]; + foreach (int tripIx in tripIxs) + { + Trip trip = Trips[tripIx]; + var stopTimeIxs = StopTimesIxByTrip[trip.TripID]; + var schedules = new List(); + foreach (int stopTimeIx in stopTimeIxs) + { + StopTime stopTime = StopTimes[stopTimeIx]; + schedules.Add(new StopTimeResponse(stopTime.StopID, stopTime.Arrival, stopTime.Departure)); + } + trips.Add(new TripResponse(trip.TripID, trip.RouteID, trip.ServiceID, schedules)); + } + } + return trips; + } +} + +public struct StopTimeResponse +{ + public string stop_id { get; } + public string arrival_time { get; } + public string departure_time { get; } + public StopTimeResponse(string stopID, string arrival, string departure) + { + stop_id = stopID; + arrival_time = arrival; + departure_time = departure; + } +} + +public struct TripResponse +{ + public string trip_id { get; } + public string route_id { get; } + public string service_id { get; } + public List schedules { get; } + public TripResponse(string tripID, string routeID, string serviceID, List stop_time_responses) + { + trip_id = tripID; + route_id = routeID; + service_id = serviceID; + schedules = stop_time_responses; + } +} + +struct Trip +{ + public Trip(string tripID, string routeID, string serviceID) + { + TripID = tripID; + RouteID = routeID; + ServiceID = serviceID; + } + public string TripID { get; } + public string RouteID { get; } + public string ServiceID { get; } +} + +struct StopTime +{ + public StopTime(string tripID, string stopID, string arrival, string departure) + { + TripID = tripID; + StopID = stopID; + Arrival = arrival; + Departure = departure; + } + + public string TripID { get; } + public string StopID { get; } + public string Arrival { get; } + public string Departure { get; } +} + +class GTFS +{ + private const string RootDir = "."; + public static (List, Dictionary>) LoadTrips() + { + string[] lines = System.IO.File.ReadAllLines(Path.Join(RootDir, "MBTA_GTFS", "/trips.txt")); + string[] header = lines[0].Split(","); + Debug.Assert(header[0] == "route_id"); + Debug.Assert(header[1] == "service_id"); + Debug.Assert(header[2] == "trip_id"); + + var trips = new List(); + var tripsIxByRoute = new Dictionary>(); + + var i = 0; + foreach (string line in lines.Skip(1)) + { + string[] cells = line.Split(","); + string routeID = cells[0]; + trips.Add(new Trip(cells[2], routeID, cells[1])); + + if (tripsIxByRoute.ContainsKey(routeID)) + { + tripsIxByRoute[routeID].Add(i); + } + else + { + tripsIxByRoute.Add(routeID, new List { i }); + } + + i++; + } + return (trips, tripsIxByRoute); + } + + public static (List, Dictionary>) LoadStopTimes() + { + var watch = new System.Diagnostics.Stopwatch(); + watch.Start(); + string[] lines = System.IO.File.ReadAllLines(Path.Join(RootDir, "MBTA_GTFS", "stop_times.txt")); + string[] header = lines[0].Split(","); + Debug.Assert(header[0] == "trip_id"); + Debug.Assert(header[1] == "arrival_time"); + Debug.Assert(header[2] == "departure_time"); + Debug.Assert(header[3] == "stop_id"); + + var stopTimes = new List(); + var stopTimesIxByTrip = new Dictionary>(); + + int i = 0; + foreach (string line in lines.Skip(1)) + { + string[] cells = line.Split(","); + var tripID = cells[0]; + stopTimes.Add(new StopTime(tripID, cells[3], cells[1], cells[2])); + + if (stopTimesIxByTrip.ContainsKey(tripID)) + { + stopTimesIxByTrip[tripID].Add(i); + } + else + { + stopTimesIxByTrip.Add(tripID, new List { i }); + } + + i++; + } + watch.Stop(); + Console.WriteLine($"loaded stop_times.txt in {watch.ElapsedMilliseconds} ms"); + return (stopTimes, stopTimesIxByTrip); + } +} diff --git a/Trannet.Benchmark/TrannetVersions/_02_ListAndDictionaryUse/GTFS.cs b/Trannet.Benchmark/TrannetVersions/_02_ListAndDictionaryUse/GTFS.cs new file mode 100644 index 0000000..bebc591 --- /dev/null +++ b/Trannet.Benchmark/TrannetVersions/_02_ListAndDictionaryUse/GTFS.cs @@ -0,0 +1,76 @@ +using System.Diagnostics; + +namespace Trannet.Benchmark.TrannetVersions._02_ListAndDictionaryUse; + +class GTFS +{ + private const string RootDir = "."; + public static (List, Dictionary>) LoadTrips() + { + string[] lines = System.IO.File.ReadAllLines(Path.Join(RootDir, "MBTA_GTFS", "/trips.txt")); + string[] header = lines[0].Split(","); + Debug.Assert(header[0] == "route_id"); + Debug.Assert(header[1] == "service_id"); + Debug.Assert(header[2] == "trip_id"); + + var trips = new List(lines.Length); + var tripsIxByRoute = new Dictionary>(100_000); + + for (var i = 1; i < lines.Length; i++) + { + var line = lines[i]; + + string[] cells = line.Split(',', 4); + string routeID = cells[0]; + trips.Add(new Trip(cells[2], routeID, cells[1])); + + if (tripsIxByRoute.TryGetValue(routeID, out var list)) + { + list.Add(i); + } + else + { + tripsIxByRoute.Add(routeID, new List { i }); + } + + } + + return (trips, tripsIxByRoute); + } + + public static (List, Dictionary>) LoadStopTimes() + { + var watch = new System.Diagnostics.Stopwatch(); + watch.Start(); + string[] lines = System.IO.File.ReadAllLines(Path.Join(RootDir, "MBTA_GTFS", "stop_times.txt")); + string[] header = lines[0].Split(","); + Debug.Assert(header[0] == "trip_id"); + Debug.Assert(header[1] == "arrival_time"); + Debug.Assert(header[2] == "departure_time"); + Debug.Assert(header[3] == "stop_id"); + + var stopTimes = new List(lines.Length); + var stopTimesIxByTrip = new Dictionary>(100_000); + + for (var i = 1; i < lines.Length; i++) + { + var line = lines[i]; + string[] cells = line.Split(',', 5); + var tripID = cells[0]; + stopTimes.Add(new StopTime(tripID, cells[3], cells[1], cells[2])); + + if (stopTimesIxByTrip.TryGetValue(tripID, out var list)) + { + list.Add(i); + } + else + { + stopTimesIxByTrip.Add(tripID, new List { i }); + } + } + + watch.Stop(); + //Console.WriteLine($"loaded stop_times.txt in {watch.ElapsedMilliseconds} ms"); + return (stopTimes, stopTimesIxByTrip); + } +} diff --git a/Trannet.Benchmark/TrannetVersions/_02_ListAndDictionaryUse/GTFSService.cs b/Trannet.Benchmark/TrannetVersions/_02_ListAndDictionaryUse/GTFSService.cs new file mode 100644 index 0000000..d5186d2 --- /dev/null +++ b/Trannet.Benchmark/TrannetVersions/_02_ListAndDictionaryUse/GTFSService.cs @@ -0,0 +1,38 @@ + +namespace Trannet.Benchmark.TrannetVersions._02_ListAndDictionaryUse; + +public static class GTFSService +{ + static List Trips { get; } + static Dictionary> TripsIxByRoute { get; } + static List StopTimes { get; } + static Dictionary> StopTimesIxByTrip { get; } + + static GTFSService() + { + (Trips, TripsIxByRoute) = GTFS.LoadTrips(); + (StopTimes, StopTimesIxByTrip) = GTFS.LoadStopTimes(); + } + + public static List SchedulesForRoute(string route) + { + var trips = new List(); + if (TripsIxByRoute.ContainsKey(route)) + { + var tripIxs = TripsIxByRoute[route]; + foreach (int tripIx in tripIxs) + { + Trip trip = Trips[tripIx]; + var stopTimeIxs = StopTimesIxByTrip[trip.TripID]; + var schedules = new List(); + foreach (int stopTimeIx in stopTimeIxs) + { + StopTime stopTime = StopTimes[stopTimeIx]; + schedules.Add(new StopTimeResponse(stopTime.StopID, stopTime.Arrival, stopTime.Departure)); + } + trips.Add(new TripResponse(trip.TripID, trip.RouteID, trip.ServiceID, schedules)); + } + } + return trips; + } +} \ No newline at end of file diff --git a/Trannet.Benchmark/TrannetVersions/_02_ListAndDictionaryUse/Models.cs b/Trannet.Benchmark/TrannetVersions/_02_ListAndDictionaryUse/Models.cs new file mode 100644 index 0000000..351c59e --- /dev/null +++ b/Trannet.Benchmark/TrannetVersions/_02_ListAndDictionaryUse/Models.cs @@ -0,0 +1,56 @@ +namespace Trannet.Benchmark.TrannetVersions._02_ListAndDictionaryUse; + +struct StopTime +{ + public StopTime(string tripID, string stopID, string arrival, string departure) + { + TripID = tripID; + StopID = stopID; + Arrival = arrival; + Departure = departure; + } + + public string TripID { get; } + public string StopID { get; } + public string Arrival { get; } + public string Departure { get; } +} +public struct StopTimeResponse +{ + public string stop_id { get; } + public string arrival_time { get; } + public string departure_time { get; } + public StopTimeResponse(string stopID, string arrival, string departure) + { + stop_id = stopID; + arrival_time = arrival; + departure_time = departure; + } +} +struct Trip +{ + public Trip(string tripID, string routeID, string serviceID) + { + TripID = tripID; + RouteID = routeID; + ServiceID = serviceID; + } + public string TripID { get; } + public string RouteID { get; } + public string ServiceID { get; } +} + +public struct TripResponse +{ + public string trip_id { get; } + public string route_id { get; } + public string service_id { get; } + public List schedules { get; } + public TripResponse(string tripID, string routeID, string serviceID, List stop_time_responses) + { + trip_id = tripID; + route_id = routeID; + service_id = serviceID; + schedules = stop_time_responses; + } +} \ No newline at end of file diff --git a/Trannet.Benchmark/TrannetVersions/_03_CacheFriendly/GTFS.cs b/Trannet.Benchmark/TrannetVersions/_03_CacheFriendly/GTFS.cs new file mode 100644 index 0000000..31ea744 --- /dev/null +++ b/Trannet.Benchmark/TrannetVersions/_03_CacheFriendly/GTFS.cs @@ -0,0 +1,110 @@ +using System.Diagnostics; +using System.Text; + +namespace Trannet.Benchmark.TrannetVersions._03_CacheFriendly; + +class GTFS +{ + private const string RootDir = "."; + public static (List, Dictionary>) LoadTrips() + { + var trips = new List(); + var tripsIxByRoute = new Dictionary>(); + using var fs = File.Open(Path.Join(RootDir, "MBTA_GTFS", "/trips.txt"), FileMode.Open, FileAccess.Read, FileShare.Read); + using var reader = new StreamReader(fs, Encoding.ASCII); + + string line = reader.ReadLine(); + string[] header = line.Split(","); + Debug.Assert(header[0] == "route_id"); + Debug.Assert(header[1] == "service_id"); + Debug.Assert(header[2] == "trip_id"); + + // Process the lines in a stream instead of loading all up front, this way we utilize the processor cache better + while ((line = reader.ReadLine()) != null) + { + string[] cells = line.Split(',', 4); + string routeID = cells[0]; + trips.Add(new Trip(cells[2], routeID, cells[1])); + + if (tripsIxByRoute.TryGetValue(routeID, out var list)) + { + list.Add(trips.Count - 1); + } + else + { + tripsIxByRoute.Add(routeID, new List { trips.Count - 1 }); + } + } + + + return (trips, tripsIxByRoute); + } + + public static (List, Dictionary>) LoadStopTimes() + { + var stopTimes = new List(); + var stopTimesIxByTrip = new Dictionary>(); + using var fs = File.Open(Path.Join(RootDir, "MBTA_GTFS", "/stop_times.txt"), FileMode.Open, FileAccess.Read, FileShare.Read); + using var reader = new StreamReader(fs, Encoding.ASCII); + + string line = reader.ReadLine(); + string[] header = line.Split(","); + Debug.Assert(header[0] == "trip_id"); + Debug.Assert(header[1] == "arrival_time"); + Debug.Assert(header[2] == "departure_time"); + Debug.Assert(header[3] == "stop_id"); + + // Process the lines in a stream instead of loading all up front, this way we utilize the processor cache better + while ((line = reader.ReadLine()) != null) + { + string[] cells = line.Split(',', 5); + var tripID = cells[0]; + stopTimes.Add(new StopTime(tripID, cells[3], cells[1], cells[2])); + + if (stopTimesIxByTrip.TryGetValue(tripID, out var list)) + { + list.Add(stopTimes.Count - 1); + } + else + { + stopTimesIxByTrip.Add(tripID, new List { stopTimes.Count - 1 }); + } + } + + + + //var watch = new System.Diagnostics.Stopwatch(); + //watch.Start(); + //string[] lines = System.IO.File.ReadAllLines(Path.Join(RootDir, "MBTA_GTFS", "stop_times.txt")); + //string[] header = lines[0].Split(","); + //Debug.Assert(header[0] == "trip_id"); + //Debug.Assert(header[1] == "arrival_time"); + //Debug.Assert(header[2] == "departure_time"); + //Debug.Assert(header[3] == "stop_id"); + + //var stopTimes = new List(lines.Length); + //var stopTimesIxByTrip = new Dictionary>(100_000); + + //for (var i = 1; i < lines.Length; i++) + //{ + // var line = lines[i]; + // string[] cells = line.Split(',', 5); + // var tripID = cells[0]; + // stopTimes.Add(new StopTime(tripID, cells[3], cells[1], cells[2])); + + // if (stopTimesIxByTrip.TryGetValue(tripID, out var list)) + // { + // list.Add(i); + // } + // else + // { + // stopTimesIxByTrip.Add(tripID, new List { i }); + // } + //} + + //watch.Stop(); + ////Console.WriteLine($"loaded stop_times.txt in {watch.ElapsedMilliseconds} ms"); + //Console.WriteLine(stopTimesIxByTrip.Count); + return (stopTimes, stopTimesIxByTrip); + } +} diff --git a/Trannet.Benchmark/TrannetVersions/_03_CacheFriendly/GTFSService.cs b/Trannet.Benchmark/TrannetVersions/_03_CacheFriendly/GTFSService.cs new file mode 100644 index 0000000..c11dad9 --- /dev/null +++ b/Trannet.Benchmark/TrannetVersions/_03_CacheFriendly/GTFSService.cs @@ -0,0 +1,38 @@ + +namespace Trannet.Benchmark.TrannetVersions._03_CacheFriendly; + +public static class GTFSService +{ + static List Trips { get; } + static Dictionary> TripsIxByRoute { get; } + static List StopTimes { get; } + static Dictionary> StopTimesIxByTrip { get; } + + static GTFSService() + { + (Trips, TripsIxByRoute) = GTFS.LoadTrips(); + (StopTimes, StopTimesIxByTrip) = GTFS.LoadStopTimes(); + } + + public static List SchedulesForRoute(string route) + { + var trips = new List(); + if (TripsIxByRoute.ContainsKey(route)) + { + var tripIxs = TripsIxByRoute[route]; + foreach (int tripIx in tripIxs) + { + Trip trip = Trips[tripIx]; + var stopTimeIxs = StopTimesIxByTrip[trip.TripID]; + var schedules = new List(); + foreach (int stopTimeIx in stopTimeIxs) + { + StopTime stopTime = StopTimes[stopTimeIx]; + schedules.Add(new StopTimeResponse(stopTime.StopID, stopTime.Arrival, stopTime.Departure)); + } + trips.Add(new TripResponse(trip.TripID, trip.RouteID, trip.ServiceID, schedules)); + } + } + return trips; + } +} \ No newline at end of file diff --git a/Trannet.Benchmark/TrannetVersions/_03_CacheFriendly/Models.cs b/Trannet.Benchmark/TrannetVersions/_03_CacheFriendly/Models.cs new file mode 100644 index 0000000..c667a84 --- /dev/null +++ b/Trannet.Benchmark/TrannetVersions/_03_CacheFriendly/Models.cs @@ -0,0 +1,56 @@ +namespace Trannet.Benchmark.TrannetVersions._03_CacheFriendly; + +struct StopTime +{ + public StopTime(string tripID, string stopID, string arrival, string departure) + { + TripID = tripID; + StopID = stopID; + Arrival = arrival; + Departure = departure; + } + + public string TripID { get; } + public string StopID { get; } + public string Arrival { get; } + public string Departure { get; } +} +public struct StopTimeResponse +{ + public string stop_id { get; } + public string arrival_time { get; } + public string departure_time { get; } + public StopTimeResponse(string stopID, string arrival, string departure) + { + stop_id = stopID; + arrival_time = arrival; + departure_time = departure; + } +} +struct Trip +{ + public Trip(string tripID, string routeID, string serviceID) + { + TripID = tripID; + RouteID = routeID; + ServiceID = serviceID; + } + public string TripID { get; } + public string RouteID { get; } + public string ServiceID { get; } +} + +public struct TripResponse +{ + public string trip_id { get; } + public string route_id { get; } + public string service_id { get; } + public List schedules { get; } + public TripResponse(string tripID, string routeID, string serviceID, List stop_time_responses) + { + trip_id = tripID; + route_id = routeID; + service_id = serviceID; + schedules = stop_time_responses; + } +} \ No newline at end of file diff --git a/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFS.cs b/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFS.cs new file mode 100644 index 0000000..750c04d --- /dev/null +++ b/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFS.cs @@ -0,0 +1,110 @@ +using System.Diagnostics; +using System.Text; + +namespace Trannet.Benchmark.TrannetVersions._11_StructFix; + +class GTFS +{ + private const string RootDir = "."; + public static (List, Dictionary>) LoadTrips() + { + var trips = new List(); + var tripsIxByRoute = new Dictionary>(); + using var fs = File.Open(Path.Join(RootDir, "MBTA_GTFS", "/trips.txt"), FileMode.Open, FileAccess.Read, FileShare.Read); + using var reader = new StreamReader(fs, Encoding.ASCII); + + string line = reader.ReadLine(); + string[] header = line.Split(","); + Debug.Assert(header[0] == "route_id"); + Debug.Assert(header[1] == "service_id"); + Debug.Assert(header[2] == "trip_id"); + + // Process the lines in a stream instead of loading all up front, this way we utilize the processor cache better + while ((line = reader.ReadLine()) != null) + { + string[] cells = line.Split(',', 4); + string routeID = cells[0]; + trips.Add(new Trip(cells[2], routeID, cells[1])); + + if (tripsIxByRoute.TryGetValue(routeID, out var list)) + { + list.Add(trips.Count - 1); + } + else + { + tripsIxByRoute.Add(routeID, new List { trips.Count - 1 }); + } + } + + + return (trips, tripsIxByRoute); + } + + public static (List, Dictionary>) LoadStopTimes() + { + var stopTimes = new List(); + var stopTimesIxByTrip = new Dictionary>(); + using var fs = File.Open(Path.Join(RootDir, "MBTA_GTFS", "/stop_times.txt"), FileMode.Open, FileAccess.Read, FileShare.Read); + using var reader = new StreamReader(fs, Encoding.ASCII); + + string line = reader.ReadLine(); + string[] header = line.Split(","); + Debug.Assert(header[0] == "trip_id"); + Debug.Assert(header[1] == "arrival_time"); + Debug.Assert(header[2] == "departure_time"); + Debug.Assert(header[3] == "stop_id"); + + // Process the lines in a stream instead of loading all up front, this way we utilize the processor cache better + while ((line = reader.ReadLine()) != null) + { + string[] cells = line.Split(',', 5); + var tripID = cells[0]; + stopTimes.Add(new StopTime(tripID, cells[3], cells[1], cells[2])); + + if (stopTimesIxByTrip.TryGetValue(tripID, out var list)) + { + list.Add(stopTimes.Count - 1); + } + else + { + stopTimesIxByTrip.Add(tripID, new List { stopTimes.Count - 1 }); + } + } + + + + //var watch = new System.Diagnostics.Stopwatch(); + //watch.Start(); + //string[] lines = System.IO.File.ReadAllLines(Path.Join(RootDir, "MBTA_GTFS", "stop_times.txt")); + //string[] header = lines[0].Split(","); + //Debug.Assert(header[0] == "trip_id"); + //Debug.Assert(header[1] == "arrival_time"); + //Debug.Assert(header[2] == "departure_time"); + //Debug.Assert(header[3] == "stop_id"); + + //var stopTimes = new List(lines.Length); + //var stopTimesIxByTrip = new Dictionary>(100_000); + + //for (var i = 1; i < lines.Length; i++) + //{ + // var line = lines[i]; + // string[] cells = line.Split(',', 5); + // var tripID = cells[0]; + // stopTimes.Add(new StopTime(tripID, cells[3], cells[1], cells[2])); + + // if (stopTimesIxByTrip.TryGetValue(tripID, out var list)) + // { + // list.Add(i); + // } + // else + // { + // stopTimesIxByTrip.Add(tripID, new List { i }); + // } + //} + + //watch.Stop(); + ////Console.WriteLine($"loaded stop_times.txt in {watch.ElapsedMilliseconds} ms"); + //Console.WriteLine(stopTimesIxByTrip.Count); + return (stopTimes, stopTimesIxByTrip); + } +} diff --git a/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFSService.cs b/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFSService.cs new file mode 100644 index 0000000..80c0606 --- /dev/null +++ b/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFSService.cs @@ -0,0 +1,43 @@ +using System.Runtime.InteropServices; +using System.Linq; + +namespace Trannet.Benchmark.TrannetVersions._11_StructFix; + +public static class GTFSService +{ + static List Trips { get; } + static Dictionary> TripsIxByRoute { get; } + static List StopTimes { get; } + static Dictionary> StopTimesIxByTrip { get; } + + static GTFSService() + { + (Trips, TripsIxByRoute) = GTFS.LoadTrips(); + (StopTimes, StopTimesIxByTrip) = GTFS.LoadStopTimes(); + } + + public static List SchedulesForRoute(string route) + { + var ret = new List(10_000); + if (TripsIxByRoute.TryGetValue(route, out var tripIxs)) + { + var stopTimes = CollectionsMarshal.AsSpan(StopTimes); + var trips = CollectionsMarshal.AsSpan(Trips); + + foreach (var tripIx in CollectionsMarshal.AsSpan(tripIxs)) + { + ref var trip = ref trips[tripIx]; + var stopTimeIxs = StopTimesIxByTrip[trip.TripID]; + + var schedules = new List(stopTimeIxs.Count); + foreach (int stopTimeIx in CollectionsMarshal.AsSpan(stopTimeIxs)) + { + schedules.Add(new StopTimeResponse(ref stopTimes[stopTimeIx])); + } + + ret.Add(new TripResponse(ref trip, schedules)); + } + } + return ret; + } +} \ No newline at end of file diff --git a/Trannet.Benchmark/TrannetVersions/_11_StructFix/Models.cs b/Trannet.Benchmark/TrannetVersions/_11_StructFix/Models.cs new file mode 100644 index 0000000..97e065c --- /dev/null +++ b/Trannet.Benchmark/TrannetVersions/_11_StructFix/Models.cs @@ -0,0 +1,59 @@ +namespace Trannet.Benchmark.TrannetVersions._11_StructFix; + +public readonly struct StopTime +{ + public StopTime(string tripID, string stopID, string arrival, string departure) + { + TripID = tripID; + StopID = stopID; + Arrival = arrival; + Departure = departure; + } + + public readonly string TripID; + public readonly string StopID; + public readonly string Arrival; + public readonly string Departure; +} +public readonly struct StopTimeResponse +{ + public readonly string stop_id; + public readonly string arrival_time; + public readonly string departure_time; + + public StopTimeResponse(ref StopTime stopTime) + { + stop_id = stopTime.StopID; + arrival_time = stopTime.Arrival; + departure_time = stopTime.Departure; + } +} + +public readonly struct Trip +{ + public Trip(string tripID, string routeID, string serviceID) + { + TripID = tripID; + RouteID = routeID; + ServiceID = serviceID; + } + + public readonly string TripID; + public readonly string RouteID; + public readonly string ServiceID; +} + +public class TripResponse +{ + public readonly string trip_id; + public readonly string route_id; + public readonly string service_id; + public readonly List schedules; + public TripResponse(ref Trip trip, List stop_time_responses) + { + trip_id = trip.TripID; + route_id = trip.RouteID; + service_id = trip.ServiceID; + schedules = stop_time_responses; + } +} \ No newline at end of file diff --git a/Trannet/.gitignore b/Trannet/.gitignore new file mode 100644 index 0000000..8a30d25 --- /dev/null +++ b/Trannet/.gitignore @@ -0,0 +1,398 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.tlog +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio 6 auto-generated project file (contains which files were open etc.) +*.vbp + +# Visual Studio 6 workspace and project file (working project files containing files to include in project) +*.dsw +*.dsp + +# Visual Studio 6 technical files +*.ncb +*.aps + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# Visual Studio History (VSHistory) files +.vshistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd + +# VS Code files for those working on multiple tools +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json +*.code-workspace + +# Local History for Visual Studio Code +.history/ + +# Windows Installer files from build outputs +*.cab +*.msi +*.msix +*.msm +*.msp + +# JetBrains Rider +*.sln.iml diff --git a/Trannet/Program.cs b/Trannet/Program.cs index 6e3fd1b..fa31aaa 100644 --- a/Trannet/Program.cs +++ b/Trannet/Program.cs @@ -4,19 +4,19 @@ builder.Services.AddControllers(); // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -builder.Services.AddEndpointsApiExplorer(); -builder.Services.AddSwaggerGen(); +//builder.Services.AddEndpointsApiExplorer(); +//builder.Services.AddSwaggerGen(); var app = builder.Build(); -// Configure the HTTP request pipeline. -if (app.Environment.IsDevelopment()) -{ - app.UseSwagger(); - app.UseSwaggerUI(); -} +//// Configure the HTTP request pipeline. +//if (app.Environment.IsDevelopment()) +//{ +// app.UseSwagger(); +// app.UseSwaggerUI(); +//} -app.UseAuthorization(); +//app.UseAuthorization(); app.MapControllers(); diff --git a/Trannet/Trannet.csproj b/Trannet/Trannet.csproj index 4289e82..cd76c9c 100644 --- a/Trannet/Trannet.csproj +++ b/Trannet/Trannet.csproj @@ -1,7 +1,7 @@ - net6.0 + net6.0;net7.0 enable enable @@ -10,4 +10,5 @@ + diff --git a/Trannet/Trannet.sln b/Trannet/Trannet.sln new file mode 100644 index 0000000..eee50a3 --- /dev/null +++ b/Trannet/Trannet.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.3.32901.215 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Trannet", "Trannet.csproj", "{57390E27-E9EF-4677-BA56-12FD9A697A14}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Trannet.Benchmark", "..\Trannet.Benchmark\Trannet.Benchmark.csproj", "{5F3602A6-AF81-4CCD-ACB6-F9FDF59AC21B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {57390E27-E9EF-4677-BA56-12FD9A697A14}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {57390E27-E9EF-4677-BA56-12FD9A697A14}.Debug|Any CPU.Build.0 = Debug|Any CPU + {57390E27-E9EF-4677-BA56-12FD9A697A14}.Release|Any CPU.ActiveCfg = Release|Any CPU + {57390E27-E9EF-4677-BA56-12FD9A697A14}.Release|Any CPU.Build.0 = Release|Any CPU + {5F3602A6-AF81-4CCD-ACB6-F9FDF59AC21B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5F3602A6-AF81-4CCD-ACB6-F9FDF59AC21B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5F3602A6-AF81-4CCD-ACB6-F9FDF59AC21B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5F3602A6-AF81-4CCD-ACB6-F9FDF59AC21B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1DD41215-A419-4650-ADDF-ABB7B2E3D0A5} + EndGlobalSection +EndGlobal diff --git a/Trannet/obj/Trannet.csproj.nuget.dgspec.json b/Trannet/obj/Trannet.csproj.nuget.dgspec.json index b9f17bb..bb45d91 100644 --- a/Trannet/obj/Trannet.csproj.nuget.dgspec.json +++ b/Trannet/obj/Trannet.csproj.nuget.dgspec.json @@ -1,31 +1,44 @@ { "format": 1, "restore": { - "/Users/gabedurazo/projects/transit-lang-cmp/Trannet/Trannet.csproj": {} + "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj": {} }, "projects": { - "/Users/gabedurazo/projects/transit-lang-cmp/Trannet/Trannet.csproj": { + "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj": { "version": "1.0.0", "restore": { - "projectUniqueName": "/Users/gabedurazo/projects/transit-lang-cmp/Trannet/Trannet.csproj", + "projectUniqueName": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj", "projectName": "Trannet", - "projectPath": "/Users/gabedurazo/projects/transit-lang-cmp/Trannet/Trannet.csproj", - "packagesPath": "/Users/gabedurazo/.nuget/packages/", - "outputPath": "/Users/gabedurazo/projects/transit-lang-cmp/Trannet/obj/", + "projectPath": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj", + "packagesPath": "C:\\Users\\Tedd\\.nuget\\packages\\", + "outputPath": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\obj\\", "projectStyle": "PackageReference", + "crossTargeting": true, + "fallbackFolders": [ + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" + ], "configFilePaths": [ - "/Users/gabedurazo/.nuget/NuGet/NuGet.Config" + "C:\\Users\\Tedd\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" ], "originalTargetFrameworks": [ - "net6.0" + "net6.0", + "net7.0" ], "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "C:\\Program Files\\dotnet\\library-packs": {}, "https://api.nuget.org/v3/index.json": {} }, "frameworks": { "net6.0": { "targetAlias": "net6.0", "projectReferences": {} + }, + "net7.0": { + "targetAlias": "net7.0", + "projectReferences": {} } }, "warningProperties": { @@ -62,7 +75,36 @@ "privateAssets": "all" } }, - "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/6.0.402/RuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\7.0.100-rc.1.22431.12\\RuntimeIdentifierGraph.json" + }, + "net7.0": { + "targetAlias": "net7.0", + "dependencies": { + "Swashbuckle.AspNetCore": { + "target": "Package", + "version": "[6.2.3, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.AspNetCore.App": { + "privateAssets": "none" + }, + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\7.0.100-rc.1.22431.12\\RuntimeIdentifierGraph.json" } } } diff --git a/Trannet/obj/Trannet.csproj.nuget.g.props b/Trannet/obj/Trannet.csproj.nuget.g.props index 9a3f675..2a9d59d 100644 --- a/Trannet/obj/Trannet.csproj.nuget.g.props +++ b/Trannet/obj/Trannet.csproj.nuget.g.props @@ -4,19 +4,30 @@ True NuGet $(MSBuildThisFileDirectory)project.assets.json - /Users/gabedurazo/.nuget/packages/ - /Users/gabedurazo/.nuget/packages/ + $(UserProfile)\.nuget\packages\ + C:\Users\Tedd\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages PackageReference - 6.3.1 + 6.3.0 - + + - - - + + - - /Users/gabedurazo/.nuget/packages/microsoft.extensions.apidescription.server/3.0.0 + + + + + + + + + + C:\Users\Tedd\.nuget\packages\microsoft.extensions.apidescription.server\3.0.0 + + + C:\Users\Tedd\.nuget\packages\microsoft.extensions.apidescription.server\3.0.0 \ No newline at end of file diff --git a/Trannet/obj/Trannet.csproj.nuget.g.targets b/Trannet/obj/Trannet.csproj.nuget.g.targets index d143396..c2aa5e7 100644 --- a/Trannet/obj/Trannet.csproj.nuget.g.targets +++ b/Trannet/obj/Trannet.csproj.nuget.g.targets @@ -1,6 +1,12 @@  - - + + + + + + + + \ No newline at end of file diff --git a/Trannet/obj/project.assets.json b/Trannet/obj/project.assets.json index 3071cb4..5df98ad 100644 --- a/Trannet/obj/project.assets.json +++ b/Trannet/obj/project.assets.json @@ -89,6 +89,95 @@ "Microsoft.AspNetCore.App" ] } + }, + "net7.0": { + "Microsoft.Extensions.ApiDescription.Server/3.0.0": { + "type": "package", + "build": { + "build/Microsoft.Extensions.ApiDescription.Server.props": {}, + "build/Microsoft.Extensions.ApiDescription.Server.targets": {} + }, + "buildMultiTargeting": { + "buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.props": {}, + "buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.targets": {} + } + }, + "Microsoft.OpenApi/1.2.3": { + "type": "package", + "compile": { + "lib/netstandard2.0/Microsoft.OpenApi.dll": { + "related": ".pdb;.xml" + } + }, + "runtime": { + "lib/netstandard2.0/Microsoft.OpenApi.dll": { + "related": ".pdb;.xml" + } + } + }, + "Swashbuckle.AspNetCore/6.2.3": { + "type": "package", + "dependencies": { + "Microsoft.Extensions.ApiDescription.Server": "3.0.0", + "Swashbuckle.AspNetCore.Swagger": "6.2.3", + "Swashbuckle.AspNetCore.SwaggerGen": "6.2.3", + "Swashbuckle.AspNetCore.SwaggerUI": "6.2.3" + }, + "build": { + "build/Swashbuckle.AspNetCore.props": {} + } + }, + "Swashbuckle.AspNetCore.Swagger/6.2.3": { + "type": "package", + "dependencies": { + "Microsoft.OpenApi": "1.2.3" + }, + "compile": { + "lib/net6.0/Swashbuckle.AspNetCore.Swagger.dll": { + "related": ".pdb;.xml" + } + }, + "runtime": { + "lib/net6.0/Swashbuckle.AspNetCore.Swagger.dll": { + "related": ".pdb;.xml" + } + }, + "frameworkReferences": [ + "Microsoft.AspNetCore.App" + ] + }, + "Swashbuckle.AspNetCore.SwaggerGen/6.2.3": { + "type": "package", + "dependencies": { + "Swashbuckle.AspNetCore.Swagger": "6.2.3" + }, + "compile": { + "lib/net6.0/Swashbuckle.AspNetCore.SwaggerGen.dll": { + "related": ".pdb;.xml" + } + }, + "runtime": { + "lib/net6.0/Swashbuckle.AspNetCore.SwaggerGen.dll": { + "related": ".pdb;.xml" + } + } + }, + "Swashbuckle.AspNetCore.SwaggerUI/6.2.3": { + "type": "package", + "compile": { + "lib/net6.0/Swashbuckle.AspNetCore.SwaggerUI.dll": { + "related": ".pdb;.xml" + } + }, + "runtime": { + "lib/net6.0/Swashbuckle.AspNetCore.SwaggerUI.dll": { + "related": ".pdb;.xml" + } + }, + "frameworkReferences": [ + "Microsoft.AspNetCore.App" + ] + } } }, "libraries": { @@ -221,33 +310,50 @@ "projectFileDependencyGroups": { "net6.0": [ "Swashbuckle.AspNetCore >= 6.2.3" + ], + "net7.0": [ + "Swashbuckle.AspNetCore >= 6.2.3" ] }, "packageFolders": { - "/Users/gabedurazo/.nuget/packages/": {} + "C:\\Users\\Tedd\\.nuget\\packages\\": {}, + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {} }, "project": { "version": "1.0.0", "restore": { - "projectUniqueName": "/Users/gabedurazo/projects/transit-lang-cmp/Trannet/Trannet.csproj", + "projectUniqueName": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj", "projectName": "Trannet", - "projectPath": "/Users/gabedurazo/projects/transit-lang-cmp/Trannet/Trannet.csproj", - "packagesPath": "/Users/gabedurazo/.nuget/packages/", - "outputPath": "/Users/gabedurazo/projects/transit-lang-cmp/Trannet/obj/", + "projectPath": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj", + "packagesPath": "C:\\Users\\Tedd\\.nuget\\packages\\", + "outputPath": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\obj\\", "projectStyle": "PackageReference", + "crossTargeting": true, + "fallbackFolders": [ + "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" + ], "configFilePaths": [ - "/Users/gabedurazo/.nuget/NuGet/NuGet.Config" + "C:\\Users\\Tedd\\AppData\\Roaming\\NuGet\\NuGet.Config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", + "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" ], "originalTargetFrameworks": [ - "net6.0" + "net6.0", + "net7.0" ], "sources": { + "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, + "C:\\Program Files\\dotnet\\library-packs": {}, "https://api.nuget.org/v3/index.json": {} }, "frameworks": { "net6.0": { "targetAlias": "net6.0", "projectReferences": {} + }, + "net7.0": { + "targetAlias": "net7.0", + "projectReferences": {} } }, "warningProperties": { @@ -284,7 +390,36 @@ "privateAssets": "all" } }, - "runtimeIdentifierGraphPath": "/usr/local/share/dotnet/sdk/6.0.402/RuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\7.0.100-rc.1.22431.12\\RuntimeIdentifierGraph.json" + }, + "net7.0": { + "targetAlias": "net7.0", + "dependencies": { + "Swashbuckle.AspNetCore": { + "target": "Package", + "version": "[6.2.3, )" + } + }, + "imports": [ + "net461", + "net462", + "net47", + "net471", + "net472", + "net48", + "net481" + ], + "assetTargetFallback": true, + "warn": true, + "frameworkReferences": { + "Microsoft.AspNetCore.App": { + "privateAssets": "none" + }, + "Microsoft.NETCore.App": { + "privateAssets": "all" + } + }, + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\7.0.100-rc.1.22431.12\\RuntimeIdentifierGraph.json" } } } diff --git a/Trannet/obj/project.nuget.cache b/Trannet/obj/project.nuget.cache index a1e5640..86e407b 100644 --- a/Trannet/obj/project.nuget.cache +++ b/Trannet/obj/project.nuget.cache @@ -1,15 +1,15 @@ { "version": 2, - "dgSpecHash": "CQxk69YVZIbnCkVRc1bfdGb+ygn+xq5uP2l83B2Ht/bQDoDd7lFpun91OlnR03PKEwFlrNKAhhOj0sHT12HNqA==", + "dgSpecHash": "dwXqvTAKziwwpkv8SiXx/X/XPit6nB9OkX1tw1tKL//AYxxfpixzX1pREhU41xWpmLvMfSAR+wwGTEkB0/9OVQ==", "success": true, - "projectFilePath": "/Users/gabedurazo/projects/transit-lang-cmp/Trannet/Trannet.csproj", + "projectFilePath": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj", "expectedPackageFiles": [ - "/Users/gabedurazo/.nuget/packages/microsoft.extensions.apidescription.server/3.0.0/microsoft.extensions.apidescription.server.3.0.0.nupkg.sha512", - "/Users/gabedurazo/.nuget/packages/microsoft.openapi/1.2.3/microsoft.openapi.1.2.3.nupkg.sha512", - "/Users/gabedurazo/.nuget/packages/swashbuckle.aspnetcore/6.2.3/swashbuckle.aspnetcore.6.2.3.nupkg.sha512", - "/Users/gabedurazo/.nuget/packages/swashbuckle.aspnetcore.swagger/6.2.3/swashbuckle.aspnetcore.swagger.6.2.3.nupkg.sha512", - "/Users/gabedurazo/.nuget/packages/swashbuckle.aspnetcore.swaggergen/6.2.3/swashbuckle.aspnetcore.swaggergen.6.2.3.nupkg.sha512", - "/Users/gabedurazo/.nuget/packages/swashbuckle.aspnetcore.swaggerui/6.2.3/swashbuckle.aspnetcore.swaggerui.6.2.3.nupkg.sha512" + "C:\\Users\\Tedd\\.nuget\\packages\\microsoft.extensions.apidescription.server\\3.0.0\\microsoft.extensions.apidescription.server.3.0.0.nupkg.sha512", + "C:\\Users\\Tedd\\.nuget\\packages\\microsoft.openapi\\1.2.3\\microsoft.openapi.1.2.3.nupkg.sha512", + "C:\\Users\\Tedd\\.nuget\\packages\\swashbuckle.aspnetcore\\6.2.3\\swashbuckle.aspnetcore.6.2.3.nupkg.sha512", + "C:\\Users\\Tedd\\.nuget\\packages\\swashbuckle.aspnetcore.swagger\\6.2.3\\swashbuckle.aspnetcore.swagger.6.2.3.nupkg.sha512", + "C:\\Users\\Tedd\\.nuget\\packages\\swashbuckle.aspnetcore.swaggergen\\6.2.3\\swashbuckle.aspnetcore.swaggergen.6.2.3.nupkg.sha512", + "C:\\Users\\Tedd\\.nuget\\packages\\swashbuckle.aspnetcore.swaggerui\\6.2.3\\swashbuckle.aspnetcore.swaggerui.6.2.3.nupkg.sha512" ], "logs": [] } \ No newline at end of file From fb8a7f5bc05cd7296a93b4dfc5b22aadea1e2f3f Mon Sep 17 00:00:00 2001 From: Tedd Hansen Date: Sun, 23 Oct 2022 20:50:36 +0200 Subject: [PATCH 2/6] Updated results --- Trannet.Benchmark/README.md | 16 ++++++++-------- Trannet.Benchmark/Trannet.Benchmark.csproj | 1 - .../TrannetVersions/_11_StructFix/GTFSService.cs | 2 +- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/Trannet.Benchmark/README.md b/Trannet.Benchmark/README.md index 7223629..56668ed 100644 --- a/Trannet.Benchmark/README.md +++ b/Trannet.Benchmark/README.md @@ -20,9 +20,9 @@ Jit=RyuJit Platform=X64 | _02_ListAndDictionaryUse | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | No | - | - | - | - | ? | | _03_CacheFriendly | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | No | - | - | - | - | ? | | | | | | | | | | | | | | | | | | | | | | | | -| _01_Original | Net70 | .NET 7.0 | 72.36 ms | 1.394 ms | 1.763 ms | 0.368 ms | 68.69 ms | 71.32 ms | 72.89 ms | 73.93 ms | 74.63 ms | 13.82 | 1.00 | 0.00 | 3 | Yes | 4285.7143 | 3142.8571 | 1285.7143 | 63080637 B | 1.00 | -| _02_ListAndDictionaryUse | Net70 | .NET 7.0 | 58.80 ms | 1.138 ms | 1.633 ms | 0.309 ms | 55.40 ms | 57.41 ms | 59.06 ms | 59.67 ms | 61.51 ms | 17.01 | 0.81 | 0.03 | 2 | No | 3500.0000 | 3375.0000 | 1125.0000 | 47521557 B | 0.75 | -| _03_CacheFriendly | Net70 | .NET 7.0 | 46.91 ms | 0.855 ms | 1.331 ms | 0.235 ms | 44.42 ms | 45.92 ms | 46.64 ms | 47.63 ms | 49.94 ms | 21.32 | 0.65 | 0.03 | 1 | No | 3272.7273 | 2454.5455 | 909.0909 | 46362841 B | 0.73 | +| _01_Original | Net70 | .NET 7.0 | 70.46 ms | 1.385 ms | 2.030 ms | 0.377 ms | 66.32 ms | 69.17 ms | 70.00 ms | 72.23 ms | 73.94 ms | 14.19 | 1.00 | 0.00 | 3 | Yes | 4285.7143 | 3142.8571 | 1285.7143 | 63080717 B | 1.00 | +| _02_ListAndDictionaryUse | Net70 | .NET 7.0 | 58.70 ms | 1.154 ms | 1.897 ms | 0.321 ms | 55.42 ms | 57.49 ms | 58.43 ms | 59.99 ms | 63.14 ms | 17.04 | 0.83 | 0.04 | 2 | No | 3444.4444 | 3333.3333 | 1111.1111 | 47521523 B | 0.75 | +| _03_CacheFriendly | Net70 | .NET 7.0 | 46.32 ms | 0.672 ms | 0.525 ms | 0.151 ms | 45.01 ms | 46.20 ms | 46.45 ms | 46.62 ms | 47.01 ms | 21.59 | 0.66 | 0.02 | 1 | No | 3272.7273 | 2454.5455 | 909.0909 | 46363628 B | 0.73 | Benchmarks with issues: LoadTripsBenchmarks._01_Original: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) @@ -37,9 +37,9 @@ Benchmarks with issues: | _02_ListAndDictionaryUse | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | No | - | - | - | - | ? | | _03_CacheFriendly | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | No | - | - | - | - | ? | | | | | | | | | | | | | | | | | | | | | | | | -| _01_Original | Net70 | .NET 7.0 | 1.477 s | 0.0267 s | 0.0237 s | 0.0063 s | 1.425 s | 1.469 s | 1.478 s | 1.489 s | 1.518 s | 0.6769 | 1.00 | 0.00 | 3 | Yes | 62000.0000 | 61000.0000 | 5000.0000 | 1157427448 B | 1.00 | -| _02_ListAndDictionaryUse | Net70 | .NET 7.0 | 1.250 s | 0.0190 s | 0.0159 s | 0.0044 s | 1.222 s | 1.240 s | 1.252 s | 1.265 s | 1.270 s | 0.8001 | 0.85 | 0.02 | 2 | No | 50000.0000 | 49000.0000 | 6000.0000 | 869224072 B | 0.75 | -| _03_CacheFriendly | Net70 | .NET 7.0 | 1.168 s | 0.0107 s | 0.0095 s | 0.0025 s | 1.156 s | 1.161 s | 1.164 s | 1.172 s | 1.186 s | 0.8562 | 0.79 | 0.02 | 1 | No | 51000.0000 | 50000.0000 | 6000.0000 | 899098344 B | 0.78 | +| _01_Original | Net70 | .NET 7.0 | 1.467 s | 0.0281 s | 0.0276 s | 0.0069 s | 1.428 s | 1.445 s | 1.466 s | 1.476 s | 1.528 s | 0.6818 | 1.00 | 0.00 | 3 | Yes | 62000.0000 | 61000.0000 | 5000.0000 | 1157427424 B | 1.00 | +| _02_ListAndDictionaryUse | Net70 | .NET 7.0 | 1.268 s | 0.0141 s | 0.0117 s | 0.0033 s | 1.239 s | 1.260 s | 1.276 s | 1.276 s | 1.280 s | 0.7887 | 0.86 | 0.02 | 2 | No | 50000.0000 | 49000.0000 | 6000.0000 | 869224088 B | 0.75 | +| _03_CacheFriendly | Net70 | .NET 7.0 | 1.181 s | 0.0146 s | 0.0136 s | 0.0035 s | 1.161 s | 1.171 s | 1.182 s | 1.189 s | 1.210 s | 0.8466 | 0.80 | 0.02 | 1 | No | 51000.0000 | 50000.0000 | 6000.0000 | 899098368 B | 0.78 | Benchmarks with issues: LoadStopTimesBenchmarks._01_Original: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) @@ -53,8 +53,8 @@ Benchmarks with issues: | _01_Original | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | - | - | - | ? | | _11_StructFix | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | - | - | - | ? | | | | | | | | | | | | | | | | | | | | | | -| _01_Original | Net70 | .NET 7.0 | 36.88 ms | 0.419 ms | 0.392 ms | 0.101 ms | 36.31 ms | 37.59 ms | 36.72 ms | 36.61 ms | 37.27 ms | 27.11 | 1.00 | 0.00 | 2 | 5000.0000 | 1214.2857 | 85135720 B | 1.00 | -| _11_StructFix | Net70 | .NET 7.0 | 23.20 ms | 0.445 ms | 0.437 ms | 0.109 ms | 22.73 ms | 24.12 ms | 23.09 ms | 22.85 ms | 23.38 ms | 43.11 | 0.63 | 0.01 | 1 | 2437.5000 | 875.0000 | 40924442 B | 0.48 | +| _01_Original | Net70 | .NET 7.0 | 36.27 ms | 0.261 ms | 0.231 ms | 0.062 ms | 35.83 ms | 36.69 ms | 36.25 ms | 36.15 ms | 36.39 ms | 27.57 | 1.00 | 0.00 | 2 | 5000.0000 | 1571.4286 | 85135720 B | 1.00 | +| _11_StructFix | Net70 | .NET 7.0 | 21.84 ms | 0.193 ms | 0.180 ms | 0.047 ms | 21.62 ms | 22.17 ms | 21.78 ms | 21.71 ms | 21.97 ms | 45.78 | 0.60 | 0.00 | 1 | 2437.5000 | 968.7500 | 40924442 B | 0.48 | Benchmarks with issues: SchedulesForRouteBenchmarks._01_Original: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) diff --git a/Trannet.Benchmark/Trannet.Benchmark.csproj b/Trannet.Benchmark/Trannet.Benchmark.csproj index b52da1b..ec596e8 100644 --- a/Trannet.Benchmark/Trannet.Benchmark.csproj +++ b/Trannet.Benchmark/Trannet.Benchmark.csproj @@ -18,5 +18,4 @@ - diff --git a/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFSService.cs b/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFSService.cs index 80c0606..215d3fa 100644 --- a/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFSService.cs +++ b/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFSService.cs @@ -23,7 +23,7 @@ public static List SchedulesForRoute(string route) { var stopTimes = CollectionsMarshal.AsSpan(StopTimes); var trips = CollectionsMarshal.AsSpan(Trips); - + foreach (var tripIx in CollectionsMarshal.AsSpan(tripIxs)) { ref var trip = ref trips[tripIx]; From 6be079965123e2337519fd7c41870ed0393f9fc1 Mon Sep 17 00:00:00 2001 From: Tedd Hansen Date: Sun, 23 Oct 2022 21:42:58 +0200 Subject: [PATCH 3/6] CSV library + simplified web api --- .../Benchmarks/TrannetBenchmarks.cs | 59 ++- Trannet.Benchmark/README.md | 53 +-- Trannet.Benchmark/Trannet.Benchmark.csproj | 1 + .../TrannetVersions/_04_CsvReader/GTFS.cs | 72 ++++ .../_04_CsvReader/GTFSService.cs | 38 ++ .../TrannetVersions/_04_CsvReader/Models.cs | 56 +++ .../TrannetVersions/_11_StructFix/GTFS.cs | 79 +--- Trannet/Controllers/SchedulesController.cs | 22 -- Trannet/Program.cs | 25 +- Trannet/Services/GTFS.cs | 71 ++++ Trannet/Services/GTFSService.cs | 189 ++-------- Trannet/Services/Models.cs | 59 +++ Trannet/Trannet.csproj | 20 +- Trannet/obj/Trannet.csproj.nuget.dgspec.json | 38 +- Trannet/obj/Trannet.csproj.nuget.g.props | 17 - Trannet/obj/Trannet.csproj.nuget.g.targets | 12 +- Trannet/obj/project.assets.json | 351 ++---------------- Trannet/obj/project.nuget.cache | 9 +- 18 files changed, 432 insertions(+), 739 deletions(-) create mode 100644 Trannet.Benchmark/TrannetVersions/_04_CsvReader/GTFS.cs create mode 100644 Trannet.Benchmark/TrannetVersions/_04_CsvReader/GTFSService.cs create mode 100644 Trannet.Benchmark/TrannetVersions/_04_CsvReader/Models.cs delete mode 100644 Trannet/Controllers/SchedulesController.cs create mode 100644 Trannet/Services/GTFS.cs create mode 100644 Trannet/Services/Models.cs diff --git a/Trannet.Benchmark/Benchmarks/TrannetBenchmarks.cs b/Trannet.Benchmark/Benchmarks/TrannetBenchmarks.cs index 7888e85..40961fa 100644 --- a/Trannet.Benchmark/Benchmarks/TrannetBenchmarks.cs +++ b/Trannet.Benchmark/Benchmarks/TrannetBenchmarks.cs @@ -27,11 +27,11 @@ class BenchmarkConfig : ManualConfig { public void AddRuntimes() { - AddDefaults(AddJob(Job.Default - .WithPlatform(Platform.X64) - .WithJit(Jit.RyuJit) - .WithRuntime(CoreRuntime.Core60) - .WithId("Net60"))); + //AddDefaults(AddJob(Job.Default + // .WithPlatform(Platform.X64) + // .WithJit(Jit.RyuJit) + // .WithRuntime(CoreRuntime.Core60) + // .WithId("Net60"))); AddDefaults(AddJob(Job.Default .WithPlatform(Platform.X64) @@ -62,50 +62,33 @@ public BenchmarkConfig() public class LoadTripsBenchmarks { [Benchmark(Baseline = true)] - public void _01_Original() - { - TrannetVersions._01_Original.GTFS.LoadTrips(); - } + public void _01_Original() => TrannetVersions._01_Original.GTFS.LoadTrips(); + [Benchmark()] + public void _02_ListAndDictionaryUse() => TrannetVersions._02_ListAndDictionaryUse.GTFS.LoadTrips(); + [Benchmark()] + public void _03_CacheFriendly() => TrannetVersions._03_CacheFriendly.GTFS.LoadTrips(); + [Benchmark()] + public void _04_CsvReader() => TrannetVersions._04_CsvReader.GTFS.LoadTrips(); - [Benchmark()] - public void _02_ListAndDictionaryUse() - { - TrannetVersions._02_ListAndDictionaryUse.GTFS.LoadTrips(); - } - - [Benchmark()] - public void _03_CacheFriendly() - { - TrannetVersions._03_CacheFriendly.GTFS.LoadTrips(); - } } -[Config(typeof(BenchmarkConfig))] [RankColumn, BaselineColumn] +[Config(typeof(BenchmarkConfig))] public class LoadStopTimesBenchmarks { [Benchmark(Baseline = true)] - public void _01_Original() - { - TrannetVersions._01_Original.GTFS.LoadStopTimes(); - } - + public void _01_Original() => TrannetVersions._01_Original.GTFS.LoadStopTimes(); + [Benchmark()] - public void _02_ListAndDictionaryUse() - { - TrannetVersions._02_ListAndDictionaryUse.GTFS.LoadStopTimes(); - } - + public void _02_ListAndDictionaryUse() => TrannetVersions._02_ListAndDictionaryUse.GTFS.LoadStopTimes(); + [Benchmark()] - public void _03_CacheFriendly() - { - TrannetVersions._03_CacheFriendly.GTFS.LoadStopTimes(); - } + public void _03_CacheFriendly() => TrannetVersions._03_CacheFriendly.GTFS.LoadStopTimes(); + [Benchmark()] + public void _04_CsvReader() => TrannetVersions._04_CsvReader.GTFS.LoadStopTimes(); + } -[MemoryDiagnoser] -[MarkdownExporter] -[MinColumn, MaxColumn, MeanColumn, MedianColumn] [Config(typeof(BenchmarkConfig))] public class SchedulesForRouteBenchmarks { diff --git a/Trannet.Benchmark/README.md b/Trannet.Benchmark/README.md index 56668ed..6ee3073 100644 --- a/Trannet.Benchmark/README.md +++ b/Trannet.Benchmark/README.md @@ -1,5 +1,7 @@ # Improvements on C# sample +There are ways to speed this up even more, for example in copying results in SchedulesForRoute. It would also be easy to throw in a Parallel.For or Parallel.ForEach. + ``` ini BenchmarkDotNet=v0.13.2, OS=Windows 11 (10.0.22000.1098/21H2) @@ -14,48 +16,25 @@ Jit=RyuJit Platform=X64 ## LoadTrips -| Method | Job | Runtime | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Ratio | RatioSD | Rank | Baseline | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio | +| Method | Job | Runtime | Mean | Error | StdDev | StdErr | Median | Min | Q1 | Q3 | Max | Op/s | Ratio | RatioSD | Rank | Baseline | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio | | ------------------------ | ----- | -------- | -------: | -------: | -------: | -------: | -------: | -------: | -------: | -------: | -------: | ----: | ----: | ------: | ---: | -------- | --------: | --------: | --------: | ---------: | ----------: | -| _01_Original | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | Yes | - | - | - | - | ? | -| _02_ListAndDictionaryUse | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | No | - | - | - | - | ? | -| _03_CacheFriendly | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | No | - | - | - | - | ? | -| | | | | | | | | | | | | | | | | | | | | | | -| _01_Original | Net70 | .NET 7.0 | 70.46 ms | 1.385 ms | 2.030 ms | 0.377 ms | 66.32 ms | 69.17 ms | 70.00 ms | 72.23 ms | 73.94 ms | 14.19 | 1.00 | 0.00 | 3 | Yes | 4285.7143 | 3142.8571 | 1285.7143 | 63080717 B | 1.00 | -| _02_ListAndDictionaryUse | Net70 | .NET 7.0 | 58.70 ms | 1.154 ms | 1.897 ms | 0.321 ms | 55.42 ms | 57.49 ms | 58.43 ms | 59.99 ms | 63.14 ms | 17.04 | 0.83 | 0.04 | 2 | No | 3444.4444 | 3333.3333 | 1111.1111 | 47521523 B | 0.75 | -| _03_CacheFriendly | Net70 | .NET 7.0 | 46.32 ms | 0.672 ms | 0.525 ms | 0.151 ms | 45.01 ms | 46.20 ms | 46.45 ms | 46.62 ms | 47.01 ms | 21.59 | 0.66 | 0.02 | 1 | No | 3272.7273 | 2454.5455 | 909.0909 | 46363628 B | 0.73 | - -Benchmarks with issues: - LoadTripsBenchmarks._01_Original: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) - LoadTripsBenchmarks._02_ListAndDictionaryUse: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) - LoadTripsBenchmarks._03_CacheFriendly: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) +| _01_Original | Net70 | .NET 7.0 | 76.14 ms | 1.491 ms | 1.939 ms | 0.396 ms | 76.13 ms | 72.95 ms | 75.03 ms | 77.77 ms | 79.44 ms | 13.13 | 1.00 | 0.00 | 4 | Yes | 4285.7143 | 3142.8571 | 1285.7143 | 63080632 B | 1.00 | +| _02_ListAndDictionaryUse | Net70 | .NET 7.0 | 60.94 ms | 1.144 ms | 1.641 ms | 0.310 ms | 60.71 ms | 58.24 ms | 59.92 ms | 61.85 ms | 64.46 ms | 16.41 | 0.80 | 0.03 | 3 | No | 3444.4444 | 3333.3333 | 1111.1111 | 47521912 B | 0.75 | +| _03_CacheFriendly | Net70 | .NET 7.0 | 48.57 ms | 0.961 ms | 1.551 ms | 0.266 ms | 48.27 ms | 45.85 ms | 47.44 ms | 49.48 ms | 51.84 ms | 20.59 | 0.64 | 0.02 | 2 | No | 3272.7273 | 2454.5455 | 909.0909 | 46362793 B | 0.73 | +| _04_CsvReader | Net70 | .NET 7.0 | 18.16 ms | 0.363 ms | 1.042 ms | 0.107 ms | 17.87 ms | 16.13 ms | 17.45 ms | 18.89 ms | 20.88 ms | 55.06 | 0.24 | 0.02 | 1 | No | 1593.7500 | 1562.5000 | 968.7500 | 17435261 B | 0.28 | ## LoadStop -| Method | Job | Runtime | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Ratio | RatioSD | Rank | Baseline | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio | -| ------------------------ | ----- | -------- | ------: | -------: | -------: | -------: | ------: | ------: | ------: | ------: | ------: | -----: | ----: | ------: | ---: | -------- | ---------: | ---------: | --------: | -----------: | ----------: | -| _01_Original | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | Yes | - | - | - | - | ? | -| _02_ListAndDictionaryUse | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | No | - | - | - | - | ? | -| _03_CacheFriendly | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | No | - | - | - | - | ? | -| | | | | | | | | | | | | | | | | | | | | | | -| _01_Original | Net70 | .NET 7.0 | 1.467 s | 0.0281 s | 0.0276 s | 0.0069 s | 1.428 s | 1.445 s | 1.466 s | 1.476 s | 1.528 s | 0.6818 | 1.00 | 0.00 | 3 | Yes | 62000.0000 | 61000.0000 | 5000.0000 | 1157427424 B | 1.00 | -| _02_ListAndDictionaryUse | Net70 | .NET 7.0 | 1.268 s | 0.0141 s | 0.0117 s | 0.0033 s | 1.239 s | 1.260 s | 1.276 s | 1.276 s | 1.280 s | 0.7887 | 0.86 | 0.02 | 2 | No | 50000.0000 | 49000.0000 | 6000.0000 | 869224088 B | 0.75 | -| _03_CacheFriendly | Net70 | .NET 7.0 | 1.181 s | 0.0146 s | 0.0136 s | 0.0035 s | 1.161 s | 1.171 s | 1.182 s | 1.189 s | 1.210 s | 0.8466 | 0.80 | 0.02 | 1 | No | 51000.0000 | 50000.0000 | 6000.0000 | 899098368 B | 0.78 | - -Benchmarks with issues: - LoadStopTimesBenchmarks._01_Original: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) - LoadStopTimesBenchmarks._02_ListAndDictionaryUse: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) - LoadStopTimesBenchmarks._03_CacheFriendly: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) +| Method | Job | Runtime | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Ratio | RatioSD | Rank | Baseline | Gen0 | Gen1 | Gen2 | Allocated | Alloc Ratio | +| ------------------------ | ----- | -------- | ---------: | -------: | -------: | ------: | ---------: | ---------: | ---------: | ---------: | ---------: | -----: | ----: | ------: | ---: | -------- | ---------: | ---------: | --------: | -----------: | ----------: | +| _01_Original | Net70 | .NET 7.0 | 1,413.4 ms | 20.09 ms | 18.79 ms | 4.85 ms | 1,385.6 ms | 1,396.9 ms | 1,416.0 ms | 1,426.8 ms | 1,443.5 ms | 0.7075 | 1.00 | 0.00 | 4 | Yes | 62000.0000 | 61000.0000 | 5000.0000 | 1157427512 B | 1.00 | +| _02_ListAndDictionaryUse | Net70 | .NET 7.0 | 1,225.3 ms | 22.11 ms | 19.60 ms | 5.24 ms | 1,180.9 ms | 1,219.1 ms | 1,223.7 ms | 1,237.7 ms | 1,254.5 ms | 0.8161 | 0.87 | 0.01 | 3 | No | 50000.0000 | 49000.0000 | 6000.0000 | 869224040 B | 0.75 | +| _03_CacheFriendly | Net70 | .NET 7.0 | 1,121.1 ms | 20.49 ms | 19.17 ms | 4.95 ms | 1,083.6 ms | 1,108.5 ms | 1,119.6 ms | 1,131.8 ms | 1,158.7 ms | 0.8920 | 0.79 | 0.02 | 2 | No | 51000.0000 | 50000.0000 | 6000.0000 | 899098456 B | 0.78 | +| _04_CsvReader | Net70 | .NET 7.0 | 684.3 ms | 12.99 ms | 12.15 ms | 3.14 ms | 657.5 ms | 678.1 ms | 685.1 ms | 689.7 ms | 706.9 ms | 1.4612 | 0.48 | 0.01 | 1 | No | 24000.0000 | 23000.0000 | 6000.0000 | 450663344 B | 0.39 | ## SchedulesForRoute -| Method | Job | Runtime | Mean | Error | StdDev | StdErr | Min | Max | Median | Q1 | Q3 | Op/s | Ratio | RatioSD | Rank | Gen0 | Gen1 | Allocated | Alloc Ratio | +| Method | Job | Runtime | Mean | Error | StdDev | StdErr | Min | Q1 | Median | Q3 | Max | Op/s | Ratio | RatioSD | Rank | Gen0 | Gen1 | Allocated | Alloc Ratio | | ------------- | ----- | -------- | -------: | -------: | -------: | -------: | -------: | -------: | -------: | -------: | -------: | ----: | ----: | ------: | ---: | --------: | --------: | ---------: | ----------: | -| _01_Original | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | - | - | - | ? | -| _11_StructFix | Net60 | .NET 6.0 | NA | NA | NA | NA | NA | NA | NA | NA | NA | NA | ? | ? | ? | - | - | - | ? | -| | | | | | | | | | | | | | | | | | | | | -| _01_Original | Net70 | .NET 7.0 | 36.27 ms | 0.261 ms | 0.231 ms | 0.062 ms | 35.83 ms | 36.69 ms | 36.25 ms | 36.15 ms | 36.39 ms | 27.57 | 1.00 | 0.00 | 2 | 5000.0000 | 1571.4286 | 85135720 B | 1.00 | -| _11_StructFix | Net70 | .NET 7.0 | 21.84 ms | 0.193 ms | 0.180 ms | 0.047 ms | 21.62 ms | 22.17 ms | 21.78 ms | 21.71 ms | 21.97 ms | 45.78 | 0.60 | 0.00 | 1 | 2437.5000 | 968.7500 | 40924442 B | 0.48 | - -Benchmarks with issues: - SchedulesForRouteBenchmarks._01_Original: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) - SchedulesForRouteBenchmarks._11_StructFix: Net60(Jit=RyuJit, Platform=X64, Runtime=.NET 6.0) +| _01_Original | Net70 | .NET 7.0 | 34.95 ms | 0.504 ms | 0.471 ms | 0.122 ms | 34.40 ms | 34.60 ms | 34.74 ms | 35.34 ms | 35.96 ms | 28.61 | 1.00 | 0.00 | 2 | 5000.0000 | 2200.0000 | 85135718 B | 1.00 | +| _11_StructFix | Net70 | .NET 7.0 | 22.62 ms | 0.448 ms | 0.461 ms | 0.112 ms | 21.77 ms | 22.34 ms | 22.77 ms | 22.96 ms | 23.44 ms | 44.21 | 0.65 | 0.02 | 1 | 2437.5000 | 1000.0000 | 40923748 B | 0.48 | diff --git a/Trannet.Benchmark/Trannet.Benchmark.csproj b/Trannet.Benchmark/Trannet.Benchmark.csproj index ec596e8..d0d8ffd 100644 --- a/Trannet.Benchmark/Trannet.Benchmark.csproj +++ b/Trannet.Benchmark/Trannet.Benchmark.csproj @@ -9,6 +9,7 @@ + diff --git a/Trannet.Benchmark/TrannetVersions/_04_CsvReader/GTFS.cs b/Trannet.Benchmark/TrannetVersions/_04_CsvReader/GTFS.cs new file mode 100644 index 0000000..de69af2 --- /dev/null +++ b/Trannet.Benchmark/TrannetVersions/_04_CsvReader/GTFS.cs @@ -0,0 +1,72 @@ +using System.Data; +using System.Diagnostics; +using System.Text; + +using Sylvan.Data.Csv; + +namespace Trannet.Benchmark.TrannetVersions._04_CsvReader; + +class GTFS +{ + private const string RootDir = "."; + public static (List, Dictionary>) LoadTrips() + { + var trips = new List(); + var tripsIxByRoute = new Dictionary>(); + var filename = Path.Join(RootDir, "MBTA_GTFS", "/trips.txt"); + var opts = new CsvDataReaderOptions { HasHeaders = true, Delimiter = ',', Culture = new System.Globalization.CultureInfo("en-US") }; + var csv = CsvDataReader.Create(filename, opts); + csv.Read(); + + // Process the lines in a stream instead of loading all up front, this way we utilize the processor cache better + // Use CSV library that specializes in CSV handling + while (csv.Read()) + { + string routeID = csv.GetString(0); + trips.Add(new Trip(csv.GetString(2), routeID, csv.GetString(1))); + + if (tripsIxByRoute.TryGetValue(routeID, out var list)) + { + list.Add(trips.Count - 1); + } + else + { + tripsIxByRoute.Add(routeID, new List { trips.Count - 1 }); + } + } + + + return (trips, tripsIxByRoute); + } + + public static (List, Dictionary>) LoadStopTimes() + { + var stopTimes = new List(); + var stopTimesIxByTrip = new Dictionary>(); + var filename = Path.Join(RootDir, "MBTA_GTFS", "/stop_times.txt"); + var opts = new CsvDataReaderOptions { HasHeaders = true, Delimiter = ',', Culture = new System.Globalization.CultureInfo("en-US") }; + var csv = CsvDataReader.Create(filename, opts); + csv.Read(); + + // Process the lines in a stream instead of loading all up front, this way we utilize the processor cache better + // Use CSV library that specializes in CSV handling + while (csv.Read()) + { + + var tripID = csv.GetString(0); + stopTimes.Add(new StopTime(tripID, csv.GetString(3), csv.GetString(1), csv.GetString(2))); + + if (stopTimesIxByTrip.TryGetValue(tripID, out var list)) + { + list.Add(stopTimes.Count - 1); + } + else + { + stopTimesIxByTrip.Add(tripID, new List { stopTimes.Count - 1 }); + } + } + csv.Close(); + + return (stopTimes, stopTimesIxByTrip); + } +} diff --git a/Trannet.Benchmark/TrannetVersions/_04_CsvReader/GTFSService.cs b/Trannet.Benchmark/TrannetVersions/_04_CsvReader/GTFSService.cs new file mode 100644 index 0000000..a359b12 --- /dev/null +++ b/Trannet.Benchmark/TrannetVersions/_04_CsvReader/GTFSService.cs @@ -0,0 +1,38 @@ + +namespace Trannet.Benchmark.TrannetVersions._04_CsvReader; + +public static class GTFSService +{ + static List Trips { get; } + static Dictionary> TripsIxByRoute { get; } + static List StopTimes { get; } + static Dictionary> StopTimesIxByTrip { get; } + + static GTFSService() + { + (Trips, TripsIxByRoute) = GTFS.LoadTrips(); + (StopTimes, StopTimesIxByTrip) = GTFS.LoadStopTimes(); + } + + public static List SchedulesForRoute(string route) + { + var trips = new List(); + if (TripsIxByRoute.ContainsKey(route)) + { + var tripIxs = TripsIxByRoute[route]; + foreach (int tripIx in tripIxs) + { + Trip trip = Trips[tripIx]; + var stopTimeIxs = StopTimesIxByTrip[trip.TripID]; + var schedules = new List(); + foreach (int stopTimeIx in stopTimeIxs) + { + StopTime stopTime = StopTimes[stopTimeIx]; + schedules.Add(new StopTimeResponse(stopTime.StopID, stopTime.Arrival, stopTime.Departure)); + } + trips.Add(new TripResponse(trip.TripID, trip.RouteID, trip.ServiceID, schedules)); + } + } + return trips; + } +} \ No newline at end of file diff --git a/Trannet.Benchmark/TrannetVersions/_04_CsvReader/Models.cs b/Trannet.Benchmark/TrannetVersions/_04_CsvReader/Models.cs new file mode 100644 index 0000000..c22dccb --- /dev/null +++ b/Trannet.Benchmark/TrannetVersions/_04_CsvReader/Models.cs @@ -0,0 +1,56 @@ +namespace Trannet.Benchmark.TrannetVersions._04_CsvReader; + +struct StopTime +{ + public StopTime(string tripID, string stopID, string arrival, string departure) + { + TripID = tripID; + StopID = stopID; + Arrival = arrival; + Departure = departure; + } + + public string TripID { get; } + public string StopID { get; } + public string Arrival { get; } + public string Departure { get; } +} +public struct StopTimeResponse +{ + public string stop_id { get; } + public string arrival_time { get; } + public string departure_time { get; } + public StopTimeResponse(string stopID, string arrival, string departure) + { + stop_id = stopID; + arrival_time = arrival; + departure_time = departure; + } +} +struct Trip +{ + public Trip(string tripID, string routeID, string serviceID) + { + TripID = tripID; + RouteID = routeID; + ServiceID = serviceID; + } + public string TripID { get; } + public string RouteID { get; } + public string ServiceID { get; } +} + +public struct TripResponse +{ + public string trip_id { get; } + public string route_id { get; } + public string service_id { get; } + public List schedules { get; } + public TripResponse(string tripID, string routeID, string serviceID, List stop_time_responses) + { + trip_id = tripID; + route_id = routeID; + service_id = serviceID; + schedules = stop_time_responses; + } +} \ No newline at end of file diff --git a/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFS.cs b/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFS.cs index 750c04d..98d517a 100644 --- a/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFS.cs +++ b/Trannet.Benchmark/TrannetVersions/_11_StructFix/GTFS.cs @@ -1,6 +1,8 @@ using System.Diagnostics; using System.Text; +using Sylvan.Data.Csv; + namespace Trannet.Benchmark.TrannetVersions._11_StructFix; class GTFS @@ -10,21 +12,17 @@ public static (List, Dictionary>) LoadTrips() { var trips = new List(); var tripsIxByRoute = new Dictionary>(); - using var fs = File.Open(Path.Join(RootDir, "MBTA_GTFS", "/trips.txt"), FileMode.Open, FileAccess.Read, FileShare.Read); - using var reader = new StreamReader(fs, Encoding.ASCII); - - string line = reader.ReadLine(); - string[] header = line.Split(","); - Debug.Assert(header[0] == "route_id"); - Debug.Assert(header[1] == "service_id"); - Debug.Assert(header[2] == "trip_id"); + var filename = Path.Join(RootDir, "MBTA_GTFS", "/trips.txt"); + var opts = new CsvDataReaderOptions { HasHeaders = true, Delimiter = ',', Culture = new System.Globalization.CultureInfo("en-US") }; + var csv = CsvDataReader.Create(filename, opts); + csv.Read(); // Process the lines in a stream instead of loading all up front, this way we utilize the processor cache better - while ((line = reader.ReadLine()) != null) + // Use CSV library that specializes in CSV handling + while (csv.Read()) { - string[] cells = line.Split(',', 4); - string routeID = cells[0]; - trips.Add(new Trip(cells[2], routeID, cells[1])); + string routeID = csv.GetString(0); + trips.Add(new Trip(csv.GetString(2), routeID, csv.GetString(1))); if (tripsIxByRoute.TryGetValue(routeID, out var list)) { @@ -44,22 +42,18 @@ public static (List, Dictionary>) LoadStopTimes() { var stopTimes = new List(); var stopTimesIxByTrip = new Dictionary>(); - using var fs = File.Open(Path.Join(RootDir, "MBTA_GTFS", "/stop_times.txt"), FileMode.Open, FileAccess.Read, FileShare.Read); - using var reader = new StreamReader(fs, Encoding.ASCII); - - string line = reader.ReadLine(); - string[] header = line.Split(","); - Debug.Assert(header[0] == "trip_id"); - Debug.Assert(header[1] == "arrival_time"); - Debug.Assert(header[2] == "departure_time"); - Debug.Assert(header[3] == "stop_id"); + var filename = Path.Join(RootDir, "MBTA_GTFS", "/stop_times.txt"); + var opts = new CsvDataReaderOptions { HasHeaders = true, Delimiter = ',', Culture = new System.Globalization.CultureInfo("en-US") }; + var csv = CsvDataReader.Create(filename, opts); + csv.Read(); // Process the lines in a stream instead of loading all up front, this way we utilize the processor cache better - while ((line = reader.ReadLine()) != null) + // Use CSV library that specializes in CSV handling + while (csv.Read()) { - string[] cells = line.Split(',', 5); - var tripID = cells[0]; - stopTimes.Add(new StopTime(tripID, cells[3], cells[1], cells[2])); + + var tripID = csv.GetString(0); + stopTimes.Add(new StopTime(tripID, csv.GetString(3), csv.GetString(1), csv.GetString(2))); if (stopTimesIxByTrip.TryGetValue(tripID, out var list)) { @@ -70,41 +64,8 @@ public static (List, Dictionary>) LoadStopTimes() stopTimesIxByTrip.Add(tripID, new List { stopTimes.Count - 1 }); } } + csv.Close(); - - - //var watch = new System.Diagnostics.Stopwatch(); - //watch.Start(); - //string[] lines = System.IO.File.ReadAllLines(Path.Join(RootDir, "MBTA_GTFS", "stop_times.txt")); - //string[] header = lines[0].Split(","); - //Debug.Assert(header[0] == "trip_id"); - //Debug.Assert(header[1] == "arrival_time"); - //Debug.Assert(header[2] == "departure_time"); - //Debug.Assert(header[3] == "stop_id"); - - //var stopTimes = new List(lines.Length); - //var stopTimesIxByTrip = new Dictionary>(100_000); - - //for (var i = 1; i < lines.Length; i++) - //{ - // var line = lines[i]; - // string[] cells = line.Split(',', 5); - // var tripID = cells[0]; - // stopTimes.Add(new StopTime(tripID, cells[3], cells[1], cells[2])); - - // if (stopTimesIxByTrip.TryGetValue(tripID, out var list)) - // { - // list.Add(i); - // } - // else - // { - // stopTimesIxByTrip.Add(tripID, new List { i }); - // } - //} - - //watch.Stop(); - ////Console.WriteLine($"loaded stop_times.txt in {watch.ElapsedMilliseconds} ms"); - //Console.WriteLine(stopTimesIxByTrip.Count); return (stopTimes, stopTimesIxByTrip); } } diff --git a/Trannet/Controllers/SchedulesController.cs b/Trannet/Controllers/SchedulesController.cs deleted file mode 100644 index d397d5c..0000000 --- a/Trannet/Controllers/SchedulesController.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Microsoft.AspNetCore.Mvc; -using Trannet.Services; - -namespace Trannet.Controllers; - -[ApiController] -[Route("[controller]")] -public class SchedulesController : ControllerBase -{ - private readonly ILogger _logger; - - public SchedulesController(ILogger logger) - { - _logger = logger; - } - - [HttpGet("{routeId}")] - public List Get(string routeId) - { - return GTFSService.SchedulesForRoute(routeId); - } -} diff --git a/Trannet/Program.cs b/Trannet/Program.cs index fa31aaa..c862543 100644 --- a/Trannet/Program.cs +++ b/Trannet/Program.cs @@ -1,23 +1,14 @@ -var builder = WebApplication.CreateBuilder(args); - -// Add services to the container. +using Trannet.Services; -builder.Services.AddControllers(); -// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle -//builder.Services.AddEndpointsApiExplorer(); -//builder.Services.AddSwaggerGen(); +// Load on startup, not on first request (would skew any benchmark). +_ = GTFSService.SchedulesForRoute(""); +// Set up web api +var builder = WebApplication.CreateBuilder(args); var app = builder.Build(); -//// Configure the HTTP request pipeline. -//if (app.Environment.IsDevelopment()) -//{ -// app.UseSwagger(); -// app.UseSwaggerUI(); -//} - -//app.UseAuthorization(); - -app.MapControllers(); +// Map URL directly to method with same signature +app.MapGet("/Schedules/{routeId}", GTFSService.SchedulesForRoute); +// Same as this: app.MapGet("/Schedules/{routeId}", (string routeId) => GTFSService.SchedulesForRoute(routeId)); app.Run(); diff --git a/Trannet/Services/GTFS.cs b/Trannet/Services/GTFS.cs new file mode 100644 index 0000000..c5e98db --- /dev/null +++ b/Trannet/Services/GTFS.cs @@ -0,0 +1,71 @@ +using System.Diagnostics; +using System.Text; + +using Sylvan.Data.Csv; + +namespace Trannet.Services; + +class GTFS +{ + private const string RootDir = "."; + public static (List, Dictionary>) LoadTrips() + { + var trips = new List(); + var tripsIxByRoute = new Dictionary>(); + var filename = Path.Join(RootDir, "MBTA_GTFS", "/trips.txt"); + var opts = new CsvDataReaderOptions { HasHeaders = true, Delimiter = ',', Culture = new System.Globalization.CultureInfo("en-US") }; + var csv = CsvDataReader.Create(filename, opts); + csv.Read(); + + // Process the lines in a stream instead of loading all up front, this way we utilize the processor cache better + // Use CSV library that specializes in CSV handling + while (csv.Read()) + { + string routeID = csv.GetString(0); + trips.Add(new Trip(csv.GetString(2), routeID, csv.GetString(1))); + + if (tripsIxByRoute.TryGetValue(routeID, out var list)) + { + list.Add(trips.Count - 1); + } + else + { + tripsIxByRoute.Add(routeID, new List { trips.Count - 1 }); + } + } + + + return (trips, tripsIxByRoute); + } + + public static (List, Dictionary>) LoadStopTimes() + { + var stopTimes = new List(); + var stopTimesIxByTrip = new Dictionary>(); + var filename = Path.Join(RootDir, "MBTA_GTFS", "/stop_times.txt"); + var opts = new CsvDataReaderOptions { HasHeaders = true, Delimiter = ',', Culture = new System.Globalization.CultureInfo("en-US") }; + var csv = CsvDataReader.Create(filename, opts); + csv.Read(); + + // Process the lines in a stream instead of loading all up front, this way we utilize the processor cache better + // Use CSV library that specializes in CSV handling + while (csv.Read()) + { + + var tripID = csv.GetString(0); + stopTimes.Add(new StopTime(tripID, csv.GetString(3), csv.GetString(1), csv.GetString(2))); + + if (stopTimesIxByTrip.TryGetValue(tripID, out var list)) + { + list.Add(stopTimes.Count - 1); + } + else + { + stopTimesIxByTrip.Add(tripID, new List { stopTimes.Count - 1 }); + } + } + csv.Close(); + + return (stopTimes, stopTimesIxByTrip); + } +} diff --git a/Trannet/Services/GTFSService.cs b/Trannet/Services/GTFSService.cs index 1d6ab66..250262c 100644 --- a/Trannet/Services/GTFSService.cs +++ b/Trannet/Services/GTFSService.cs @@ -1,168 +1,43 @@ -using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Linq; namespace Trannet.Services; public static class GTFSService { - static List Trips { get; } - static Dictionary> TripsIxByRoute { get; } - static List StopTimes { get; } - static Dictionary> StopTimesIxByTrip { get; } + static List Trips { get; } + static Dictionary> TripsIxByRoute { get; } + static List StopTimes { get; } + static Dictionary> StopTimesIxByTrip { get; } - static GTFSService() - { - (Trips, TripsIxByRoute) = GTFS.LoadTrips(); - (StopTimes, StopTimesIxByTrip) = GTFS.LoadStopTimes(); - } - - public static List SchedulesForRoute(string route) - { - var trips = new List(); - if (TripsIxByRoute.ContainsKey(route)) - { - var tripIxs = TripsIxByRoute[route]; - foreach (int tripIx in tripIxs) - { - Trip trip = Trips[tripIx]; - var stopTimeIxs = StopTimesIxByTrip[trip.TripID]; - var schedules = new List(); - foreach (int stopTimeIx in stopTimeIxs) - { - StopTime stopTime = StopTimes[stopTimeIx]; - schedules.Add(new StopTimeResponse(stopTime.StopID, stopTime.Arrival, stopTime.Departure)); - } - trips.Add(new TripResponse(trip.TripID, trip.RouteID, trip.ServiceID, schedules)); - } - } - return trips; - } -} - -public struct StopTimeResponse -{ - public string stop_id { get; } - public string arrival_time { get; } - public string departure_time { get; } - public StopTimeResponse(string stopID, string arrival, string departure) - { - stop_id = stopID; - arrival_time = arrival; - departure_time = departure; - } -} - -public struct TripResponse -{ - public string trip_id { get; } - public string route_id { get; } - public string service_id { get; } - public List schedules { get; } - public TripResponse(string tripID, string routeID, string serviceID, List stop_time_responses) - { - trip_id = tripID; - route_id = routeID; - service_id = serviceID; - schedules = stop_time_responses; - } -} - -struct Trip -{ - public Trip(string tripID, string routeID, string serviceID) - { - TripID = tripID; - RouteID = routeID; - ServiceID = serviceID; - } - public string TripID { get; } - public string RouteID { get; } - public string ServiceID { get; } -} - -struct StopTime -{ - public StopTime(string tripID, string stopID, string arrival, string departure) - { - TripID = tripID; - StopID = stopID; - Arrival = arrival; - Departure = departure; - } - - public string TripID { get; } - public string StopID { get; } - public string Arrival { get; } - public string Departure { get; } -} - -class GTFS -{ - public static (List, Dictionary>) LoadTrips() - { - string[] lines = System.IO.File.ReadAllLines(@"../MBTA_GTFS/trips.txt"); - string[] header = lines[0].Split(","); - Debug.Assert(header[0] == "route_id"); - Debug.Assert(header[1] == "service_id"); - Debug.Assert(header[2] == "trip_id"); - - var trips = new List(); - var tripsIxByRoute = new Dictionary>(); - - var i = 0; - foreach (string line in lines.Skip(1)) + static GTFSService() { - string[] cells = line.Split(","); - string routeID = cells[0]; - trips.Add(new Trip(cells[2], routeID, cells[1])); - - if (tripsIxByRoute.ContainsKey(routeID)) - { - tripsIxByRoute[routeID].Add(i); - } - else - { - tripsIxByRoute.Add(routeID, new List { i }); - } - - i++; + (Trips, TripsIxByRoute) = GTFS.LoadTrips(); + (StopTimes, StopTimesIxByTrip) = GTFS.LoadStopTimes(); } - return (trips, tripsIxByRoute); - } - public static (List, Dictionary>) LoadStopTimes() - { - var watch = new System.Diagnostics.Stopwatch(); - watch.Start(); - string[] lines = System.IO.File.ReadAllLines(@"../MBTA_GTFS/stop_times.txt"); - string[] header = lines[0].Split(","); - Debug.Assert(header[0] == "trip_id"); - Debug.Assert(header[1] == "arrival_time"); - Debug.Assert(header[2] == "departure_time"); - Debug.Assert(header[3] == "stop_id"); - - var stopTimes = new List(); - var stopTimesIxByTrip = new Dictionary>(); - - int i = 0; - foreach (string line in lines.Skip(1)) + public static List SchedulesForRoute(string route) { - string[] cells = line.Split(","); - var tripID = cells[0]; - stopTimes.Add(new StopTime(tripID, cells[3], cells[1], cells[2])); - - if (stopTimesIxByTrip.ContainsKey(tripID)) - { - stopTimesIxByTrip[tripID].Add(i); - } - else - { - stopTimesIxByTrip.Add(tripID, new List { i }); - } - - i++; + var ret = new List(10_000); + if (TripsIxByRoute.TryGetValue(route, out var tripIxs)) + { + var stopTimes = CollectionsMarshal.AsSpan(StopTimes); + var trips = CollectionsMarshal.AsSpan(Trips); + + foreach (var tripIx in CollectionsMarshal.AsSpan(tripIxs)) + { + ref var trip = ref trips[tripIx]; + var stopTimeIxs = StopTimesIxByTrip[trip.TripID]; + + var schedules = new List(stopTimeIxs.Count); + foreach (int stopTimeIx in CollectionsMarshal.AsSpan(stopTimeIxs)) + { + schedules.Add(new StopTimeResponse(ref stopTimes[stopTimeIx])); + } + + ret.Add(new TripResponse(ref trip, schedules)); + } + } + return ret; } - watch.Stop(); - Console.WriteLine($"loaded stop_times.txt in {watch.ElapsedMilliseconds} ms"); - return (stopTimes, stopTimesIxByTrip); - } -} +} \ No newline at end of file diff --git a/Trannet/Services/Models.cs b/Trannet/Services/Models.cs new file mode 100644 index 0000000..1d0738d --- /dev/null +++ b/Trannet/Services/Models.cs @@ -0,0 +1,59 @@ +namespace Trannet.Services; + +public readonly struct StopTime +{ + public StopTime(string tripID, string stopID, string arrival, string departure) + { + TripID = tripID; + StopID = stopID; + Arrival = arrival; + Departure = departure; + } + + public readonly string TripID; + public readonly string StopID; + public readonly string Arrival; + public readonly string Departure; +} +public readonly struct StopTimeResponse +{ + public readonly string stop_id; + public readonly string arrival_time; + public readonly string departure_time; + + public StopTimeResponse(ref StopTime stopTime) + { + stop_id = stopTime.StopID; + arrival_time = stopTime.Arrival; + departure_time = stopTime.Departure; + } +} + +public readonly struct Trip +{ + public Trip(string tripID, string routeID, string serviceID) + { + TripID = tripID; + RouteID = routeID; + ServiceID = serviceID; + } + + public readonly string TripID; + public readonly string RouteID; + public readonly string ServiceID; +} + +public class TripResponse +{ + public readonly string trip_id; + public readonly string route_id; + public readonly string service_id; + public readonly List schedules; + public TripResponse(ref Trip trip, List stop_time_responses) + { + trip_id = trip.TripID; + route_id = trip.RouteID; + service_id = trip.ServiceID; + schedules = stop_time_responses; + } +} \ No newline at end of file diff --git a/Trannet/Trannet.csproj b/Trannet/Trannet.csproj index cd76c9c..ca8767c 100644 --- a/Trannet/Trannet.csproj +++ b/Trannet/Trannet.csproj @@ -1,14 +1,12 @@ - - net6.0;net7.0 - enable - enable - - - - - - - + + net7.0 + enable + enable + + + + + diff --git a/Trannet/obj/Trannet.csproj.nuget.dgspec.json b/Trannet/obj/Trannet.csproj.nuget.dgspec.json index bb45d91..b216b8b 100644 --- a/Trannet/obj/Trannet.csproj.nuget.dgspec.json +++ b/Trannet/obj/Trannet.csproj.nuget.dgspec.json @@ -23,7 +23,6 @@ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" ], "originalTargetFrameworks": [ - "net6.0", "net7.0" ], "sources": { @@ -32,10 +31,6 @@ "https://api.nuget.org/v3/index.json": {} }, "frameworks": { - "net6.0": { - "targetAlias": "net6.0", - "projectReferences": {} - }, "net7.0": { "targetAlias": "net7.0", "projectReferences": {} @@ -48,41 +43,12 @@ } }, "frameworks": { - "net6.0": { - "targetAlias": "net6.0", - "dependencies": { - "Swashbuckle.AspNetCore": { - "target": "Package", - "version": "[6.2.3, )" - } - }, - "imports": [ - "net461", - "net462", - "net47", - "net471", - "net472", - "net48", - "net481" - ], - "assetTargetFallback": true, - "warn": true, - "frameworkReferences": { - "Microsoft.AspNetCore.App": { - "privateAssets": "none" - }, - "Microsoft.NETCore.App": { - "privateAssets": "all" - } - }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\7.0.100-rc.1.22431.12\\RuntimeIdentifierGraph.json" - }, "net7.0": { "targetAlias": "net7.0", "dependencies": { - "Swashbuckle.AspNetCore": { + "Sylvan.Data.Csv": { "target": "Package", - "version": "[6.2.3, )" + "version": "[1.2.3, )" } }, "imports": [ diff --git a/Trannet/obj/Trannet.csproj.nuget.g.props b/Trannet/obj/Trannet.csproj.nuget.g.props index 2a9d59d..0b5227b 100644 --- a/Trannet/obj/Trannet.csproj.nuget.g.props +++ b/Trannet/obj/Trannet.csproj.nuget.g.props @@ -13,21 +13,4 @@ - - - - - - - - - - - - - C:\Users\Tedd\.nuget\packages\microsoft.extensions.apidescription.server\3.0.0 - - - C:\Users\Tedd\.nuget\packages\microsoft.extensions.apidescription.server\3.0.0 - \ No newline at end of file diff --git a/Trannet/obj/Trannet.csproj.nuget.g.targets b/Trannet/obj/Trannet.csproj.nuget.g.targets index c2aa5e7..3dc06ef 100644 --- a/Trannet/obj/Trannet.csproj.nuget.g.targets +++ b/Trannet/obj/Trannet.csproj.nuget.g.targets @@ -1,12 +1,2 @@  - - - - - - - - - - - \ No newline at end of file + \ No newline at end of file diff --git a/Trannet/obj/project.assets.json b/Trannet/obj/project.assets.json index 5df98ad..56ea161 100644 --- a/Trannet/obj/project.assets.json +++ b/Trannet/obj/project.assets.json @@ -1,318 +1,49 @@ { "version": 3, "targets": { - "net6.0": { - "Microsoft.Extensions.ApiDescription.Server/3.0.0": { - "type": "package", - "build": { - "build/Microsoft.Extensions.ApiDescription.Server.props": {}, - "build/Microsoft.Extensions.ApiDescription.Server.targets": {} - }, - "buildMultiTargeting": { - "buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.props": {}, - "buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.targets": {} - } - }, - "Microsoft.OpenApi/1.2.3": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.OpenApi.dll": { - "related": ".pdb;.xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.OpenApi.dll": { - "related": ".pdb;.xml" - } - } - }, - "Swashbuckle.AspNetCore/6.2.3": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.ApiDescription.Server": "3.0.0", - "Swashbuckle.AspNetCore.Swagger": "6.2.3", - "Swashbuckle.AspNetCore.SwaggerGen": "6.2.3", - "Swashbuckle.AspNetCore.SwaggerUI": "6.2.3" - }, - "build": { - "build/Swashbuckle.AspNetCore.props": {} - } - }, - "Swashbuckle.AspNetCore.Swagger/6.2.3": { - "type": "package", - "dependencies": { - "Microsoft.OpenApi": "1.2.3" - }, - "compile": { - "lib/net6.0/Swashbuckle.AspNetCore.Swagger.dll": { - "related": ".pdb;.xml" - } - }, - "runtime": { - "lib/net6.0/Swashbuckle.AspNetCore.Swagger.dll": { - "related": ".pdb;.xml" - } - }, - "frameworkReferences": [ - "Microsoft.AspNetCore.App" - ] - }, - "Swashbuckle.AspNetCore.SwaggerGen/6.2.3": { - "type": "package", - "dependencies": { - "Swashbuckle.AspNetCore.Swagger": "6.2.3" - }, - "compile": { - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerGen.dll": { - "related": ".pdb;.xml" - } - }, - "runtime": { - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerGen.dll": { - "related": ".pdb;.xml" - } - } - }, - "Swashbuckle.AspNetCore.SwaggerUI/6.2.3": { - "type": "package", - "compile": { - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerUI.dll": { - "related": ".pdb;.xml" - } - }, - "runtime": { - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerUI.dll": { - "related": ".pdb;.xml" - } - }, - "frameworkReferences": [ - "Microsoft.AspNetCore.App" - ] - } - }, "net7.0": { - "Microsoft.Extensions.ApiDescription.Server/3.0.0": { - "type": "package", - "build": { - "build/Microsoft.Extensions.ApiDescription.Server.props": {}, - "build/Microsoft.Extensions.ApiDescription.Server.targets": {} - }, - "buildMultiTargeting": { - "buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.props": {}, - "buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.targets": {} - } - }, - "Microsoft.OpenApi/1.2.3": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.OpenApi.dll": { - "related": ".pdb;.xml" - } - }, - "runtime": { - "lib/netstandard2.0/Microsoft.OpenApi.dll": { - "related": ".pdb;.xml" - } - } - }, - "Swashbuckle.AspNetCore/6.2.3": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.ApiDescription.Server": "3.0.0", - "Swashbuckle.AspNetCore.Swagger": "6.2.3", - "Swashbuckle.AspNetCore.SwaggerGen": "6.2.3", - "Swashbuckle.AspNetCore.SwaggerUI": "6.2.3" - }, - "build": { - "build/Swashbuckle.AspNetCore.props": {} - } - }, - "Swashbuckle.AspNetCore.Swagger/6.2.3": { - "type": "package", - "dependencies": { - "Microsoft.OpenApi": "1.2.3" - }, - "compile": { - "lib/net6.0/Swashbuckle.AspNetCore.Swagger.dll": { - "related": ".pdb;.xml" - } - }, - "runtime": { - "lib/net6.0/Swashbuckle.AspNetCore.Swagger.dll": { - "related": ".pdb;.xml" - } - }, - "frameworkReferences": [ - "Microsoft.AspNetCore.App" - ] - }, - "Swashbuckle.AspNetCore.SwaggerGen/6.2.3": { + "Sylvan.Data.Csv/1.2.3": { "type": "package", - "dependencies": { - "Swashbuckle.AspNetCore.Swagger": "6.2.3" - }, "compile": { - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerGen.dll": { - "related": ".pdb;.xml" + "lib/net6.0/Sylvan.Data.Csv.dll": { + "related": ".xml" } }, "runtime": { - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerGen.dll": { - "related": ".pdb;.xml" + "lib/net6.0/Sylvan.Data.Csv.dll": { + "related": ".xml" } } - }, - "Swashbuckle.AspNetCore.SwaggerUI/6.2.3": { - "type": "package", - "compile": { - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerUI.dll": { - "related": ".pdb;.xml" - } - }, - "runtime": { - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerUI.dll": { - "related": ".pdb;.xml" - } - }, - "frameworkReferences": [ - "Microsoft.AspNetCore.App" - ] } } }, "libraries": { - "Microsoft.Extensions.ApiDescription.Server/3.0.0": { - "sha512": "LH4OE/76F6sOCslif7+Xh3fS/wUUrE5ryeXAMcoCnuwOQGT5Smw0p57IgDh/pHgHaGz/e+AmEQb7pRgb++wt0w==", - "type": "package", - "path": "microsoft.extensions.apidescription.server/3.0.0", - "hasTools": true, - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "build/Microsoft.Extensions.ApiDescription.Server.props", - "build/Microsoft.Extensions.ApiDescription.Server.targets", - "buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.props", - "buildMultiTargeting/Microsoft.Extensions.ApiDescription.Server.targets", - "microsoft.extensions.apidescription.server.3.0.0.nupkg.sha512", - "microsoft.extensions.apidescription.server.nuspec", - "tools/Newtonsoft.Json.dll", - "tools/dotnet-getdocument.deps.json", - "tools/dotnet-getdocument.dll", - "tools/dotnet-getdocument.runtimeconfig.json", - "tools/net461-x86/GetDocument.Insider.exe", - "tools/net461-x86/GetDocument.Insider.exe.config", - "tools/net461/GetDocument.Insider.exe", - "tools/net461/GetDocument.Insider.exe.config", - "tools/netcoreapp2.1/GetDocument.Insider.deps.json", - "tools/netcoreapp2.1/GetDocument.Insider.dll", - "tools/netcoreapp2.1/GetDocument.Insider.runtimeconfig.json" - ] - }, - "Microsoft.OpenApi/1.2.3": { - "sha512": "Nug3rO+7Kl5/SBAadzSMAVgqDlfGjJZ0GenQrLywJ84XGKO0uRqkunz5Wyl0SDwcR71bAATXvSdbdzPrYRYKGw==", - "type": "package", - "path": "microsoft.openapi/1.2.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net46/Microsoft.OpenApi.dll", - "lib/net46/Microsoft.OpenApi.pdb", - "lib/net46/Microsoft.OpenApi.xml", - "lib/netstandard2.0/Microsoft.OpenApi.dll", - "lib/netstandard2.0/Microsoft.OpenApi.pdb", - "lib/netstandard2.0/Microsoft.OpenApi.xml", - "microsoft.openapi.1.2.3.nupkg.sha512", - "microsoft.openapi.nuspec" - ] - }, - "Swashbuckle.AspNetCore/6.2.3": { - "sha512": "cnzQDn0Le+hInsw2SYwlOhOCPXpYi/szcvnyqZJ12v+QyrLBwAmWXBg6RIyHB18s/mLeywC+Rg2O9ndz0IUNYQ==", - "type": "package", - "path": "swashbuckle.aspnetcore/6.2.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "build/Swashbuckle.AspNetCore.props", - "swashbuckle.aspnetcore.6.2.3.nupkg.sha512", - "swashbuckle.aspnetcore.nuspec" - ] - }, - "Swashbuckle.AspNetCore.Swagger/6.2.3": { - "sha512": "qOF7j1sL0bWm8g/qqHVPCvkO3JlVvUIB8WfC98kSh6BT5y5DAnBNctfac7XR5EZf+eD7/WasvANncTqwZYfmWQ==", - "type": "package", - "path": "swashbuckle.aspnetcore.swagger/6.2.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net5.0/Swashbuckle.AspNetCore.Swagger.dll", - "lib/net5.0/Swashbuckle.AspNetCore.Swagger.pdb", - "lib/net5.0/Swashbuckle.AspNetCore.Swagger.xml", - "lib/net6.0/Swashbuckle.AspNetCore.Swagger.dll", - "lib/net6.0/Swashbuckle.AspNetCore.Swagger.pdb", - "lib/net6.0/Swashbuckle.AspNetCore.Swagger.xml", - "lib/netcoreapp3.0/Swashbuckle.AspNetCore.Swagger.dll", - "lib/netcoreapp3.0/Swashbuckle.AspNetCore.Swagger.pdb", - "lib/netcoreapp3.0/Swashbuckle.AspNetCore.Swagger.xml", - "lib/netstandard2.0/Swashbuckle.AspNetCore.Swagger.dll", - "lib/netstandard2.0/Swashbuckle.AspNetCore.Swagger.pdb", - "lib/netstandard2.0/Swashbuckle.AspNetCore.Swagger.xml", - "swashbuckle.aspnetcore.swagger.6.2.3.nupkg.sha512", - "swashbuckle.aspnetcore.swagger.nuspec" - ] - }, - "Swashbuckle.AspNetCore.SwaggerGen/6.2.3": { - "sha512": "+Xq7WdMCCfcXlnbLJVFNgY8ITdP2TRYIlpbt6IKzDw5FwFxdi9lBfNDtcT+/wkKwX70iBBFmXldnnd02/VO72A==", - "type": "package", - "path": "swashbuckle.aspnetcore.swaggergen/6.2.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net5.0/Swashbuckle.AspNetCore.SwaggerGen.dll", - "lib/net5.0/Swashbuckle.AspNetCore.SwaggerGen.pdb", - "lib/net5.0/Swashbuckle.AspNetCore.SwaggerGen.xml", - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerGen.dll", - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerGen.pdb", - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerGen.xml", - "lib/netcoreapp3.0/Swashbuckle.AspNetCore.SwaggerGen.dll", - "lib/netcoreapp3.0/Swashbuckle.AspNetCore.SwaggerGen.pdb", - "lib/netcoreapp3.0/Swashbuckle.AspNetCore.SwaggerGen.xml", - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerGen.dll", - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerGen.pdb", - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerGen.xml", - "swashbuckle.aspnetcore.swaggergen.6.2.3.nupkg.sha512", - "swashbuckle.aspnetcore.swaggergen.nuspec" - ] - }, - "Swashbuckle.AspNetCore.SwaggerUI/6.2.3": { - "sha512": "bCRI87uKJVb4G+KURWm8LQrL64St04dEFZcF6gIM67Zc0Sr/N47EO83ybLMYOvfNdO1DCv8xwPcrz9J/VEhQ5g==", + "Sylvan.Data.Csv/1.2.3": { + "sha512": "XNpVvpoR6pcrfgOHaDCVLm1DBthhhi5VyOhUj+Nq7421JFqsvakQlcvQdB1swLW6PeBMC6Hst1VQuUXEQDEdEQ==", "type": "package", - "path": "swashbuckle.aspnetcore.swaggerui/6.2.3", + "path": "sylvan.data.csv/1.2.3", "files": [ ".nupkg.metadata", ".signature.p7s", - "lib/net5.0/Swashbuckle.AspNetCore.SwaggerUI.dll", - "lib/net5.0/Swashbuckle.AspNetCore.SwaggerUI.pdb", - "lib/net5.0/Swashbuckle.AspNetCore.SwaggerUI.xml", - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerUI.dll", - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerUI.pdb", - "lib/net6.0/Swashbuckle.AspNetCore.SwaggerUI.xml", - "lib/netcoreapp3.0/Swashbuckle.AspNetCore.SwaggerUI.dll", - "lib/netcoreapp3.0/Swashbuckle.AspNetCore.SwaggerUI.pdb", - "lib/netcoreapp3.0/Swashbuckle.AspNetCore.SwaggerUI.xml", - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerUI.dll", - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerUI.pdb", - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerUI.xml", - "swashbuckle.aspnetcore.swaggerui.6.2.3.nupkg.sha512", - "swashbuckle.aspnetcore.swaggerui.nuspec" + "Sylvan.png", + "lib/net6.0/Sylvan.Data.Csv.dll", + "lib/net6.0/Sylvan.Data.Csv.xml", + "lib/netcoreapp3.0/Sylvan.Data.Csv.dll", + "lib/netcoreapp3.0/Sylvan.Data.Csv.xml", + "lib/netstandard2.0/Sylvan.Data.Csv.dll", + "lib/netstandard2.0/Sylvan.Data.Csv.xml", + "lib/netstandard2.1/Sylvan.Data.Csv.dll", + "lib/netstandard2.1/Sylvan.Data.Csv.xml", + "license.txt", + "readme.md", + "sylvan.data.csv.1.2.3.nupkg.sha512", + "sylvan.data.csv.nuspec" ] } }, "projectFileDependencyGroups": { - "net6.0": [ - "Swashbuckle.AspNetCore >= 6.2.3" - ], "net7.0": [ - "Swashbuckle.AspNetCore >= 6.2.3" + "Sylvan.Data.Csv >= 1.2.3" ] }, "packageFolders": { @@ -338,7 +69,6 @@ "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" ], "originalTargetFrameworks": [ - "net6.0", "net7.0" ], "sources": { @@ -347,10 +77,6 @@ "https://api.nuget.org/v3/index.json": {} }, "frameworks": { - "net6.0": { - "targetAlias": "net6.0", - "projectReferences": {} - }, "net7.0": { "targetAlias": "net7.0", "projectReferences": {} @@ -363,41 +89,12 @@ } }, "frameworks": { - "net6.0": { - "targetAlias": "net6.0", - "dependencies": { - "Swashbuckle.AspNetCore": { - "target": "Package", - "version": "[6.2.3, )" - } - }, - "imports": [ - "net461", - "net462", - "net47", - "net471", - "net472", - "net48", - "net481" - ], - "assetTargetFallback": true, - "warn": true, - "frameworkReferences": { - "Microsoft.AspNetCore.App": { - "privateAssets": "none" - }, - "Microsoft.NETCore.App": { - "privateAssets": "all" - } - }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\7.0.100-rc.1.22431.12\\RuntimeIdentifierGraph.json" - }, "net7.0": { "targetAlias": "net7.0", "dependencies": { - "Swashbuckle.AspNetCore": { + "Sylvan.Data.Csv": { "target": "Package", - "version": "[6.2.3, )" + "version": "[1.2.3, )" } }, "imports": [ diff --git a/Trannet/obj/project.nuget.cache b/Trannet/obj/project.nuget.cache index 86e407b..571381f 100644 --- a/Trannet/obj/project.nuget.cache +++ b/Trannet/obj/project.nuget.cache @@ -1,15 +1,10 @@ { "version": 2, - "dgSpecHash": "dwXqvTAKziwwpkv8SiXx/X/XPit6nB9OkX1tw1tKL//AYxxfpixzX1pREhU41xWpmLvMfSAR+wwGTEkB0/9OVQ==", + "dgSpecHash": "+ji7et497GW3FErz4ITMfi3iUmwxjna/P8RLKLGwQ9GSayoiFkv3t+TH+hm+TH0w20s4ygcqcoITW07YQMhmkA==", "success": true, "projectFilePath": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj", "expectedPackageFiles": [ - "C:\\Users\\Tedd\\.nuget\\packages\\microsoft.extensions.apidescription.server\\3.0.0\\microsoft.extensions.apidescription.server.3.0.0.nupkg.sha512", - "C:\\Users\\Tedd\\.nuget\\packages\\microsoft.openapi\\1.2.3\\microsoft.openapi.1.2.3.nupkg.sha512", - "C:\\Users\\Tedd\\.nuget\\packages\\swashbuckle.aspnetcore\\6.2.3\\swashbuckle.aspnetcore.6.2.3.nupkg.sha512", - "C:\\Users\\Tedd\\.nuget\\packages\\swashbuckle.aspnetcore.swagger\\6.2.3\\swashbuckle.aspnetcore.swagger.6.2.3.nupkg.sha512", - "C:\\Users\\Tedd\\.nuget\\packages\\swashbuckle.aspnetcore.swaggergen\\6.2.3\\swashbuckle.aspnetcore.swaggergen.6.2.3.nupkg.sha512", - "C:\\Users\\Tedd\\.nuget\\packages\\swashbuckle.aspnetcore.swaggerui\\6.2.3\\swashbuckle.aspnetcore.swaggerui.6.2.3.nupkg.sha512" + "C:\\Users\\Tedd\\.nuget\\packages\\sylvan.data.csv\\1.2.3\\sylvan.data.csv.1.2.3.nupkg.sha512" ], "logs": [] } \ No newline at end of file From 4ae61b1fda11ba7fdd7df0ca0180635473999477 Mon Sep 17 00:00:00 2001 From: Tedd Hansen Date: Sun, 23 Oct 2022 22:16:34 +0200 Subject: [PATCH 4/6] Remove obj folder --- Trannet/obj/Trannet.csproj.nuget.dgspec.json | 78 ------------ Trannet/obj/Trannet.csproj.nuget.g.props | 16 --- Trannet/obj/Trannet.csproj.nuget.g.targets | 2 - Trannet/obj/project.assets.json | 123 ------------------- Trannet/obj/project.nuget.cache | 10 -- Trannet/obj/staticwebassets.pack.sentinel | 12 -- 6 files changed, 241 deletions(-) delete mode 100644 Trannet/obj/Trannet.csproj.nuget.dgspec.json delete mode 100644 Trannet/obj/Trannet.csproj.nuget.g.props delete mode 100644 Trannet/obj/Trannet.csproj.nuget.g.targets delete mode 100644 Trannet/obj/project.assets.json delete mode 100644 Trannet/obj/project.nuget.cache delete mode 100644 Trannet/obj/staticwebassets.pack.sentinel diff --git a/Trannet/obj/Trannet.csproj.nuget.dgspec.json b/Trannet/obj/Trannet.csproj.nuget.dgspec.json deleted file mode 100644 index b216b8b..0000000 --- a/Trannet/obj/Trannet.csproj.nuget.dgspec.json +++ /dev/null @@ -1,78 +0,0 @@ -{ - "format": 1, - "restore": { - "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj": {} - }, - "projects": { - "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj": { - "version": "1.0.0", - "restore": { - "projectUniqueName": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj", - "projectName": "Trannet", - "projectPath": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj", - "packagesPath": "C:\\Users\\Tedd\\.nuget\\packages\\", - "outputPath": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\obj\\", - "projectStyle": "PackageReference", - "crossTargeting": true, - "fallbackFolders": [ - "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" - ], - "configFilePaths": [ - "C:\\Users\\Tedd\\AppData\\Roaming\\NuGet\\NuGet.Config", - "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", - "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" - ], - "originalTargetFrameworks": [ - "net7.0" - ], - "sources": { - "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, - "C:\\Program Files\\dotnet\\library-packs": {}, - "https://api.nuget.org/v3/index.json": {} - }, - "frameworks": { - "net7.0": { - "targetAlias": "net7.0", - "projectReferences": {} - } - }, - "warningProperties": { - "warnAsError": [ - "NU1605" - ] - } - }, - "frameworks": { - "net7.0": { - "targetAlias": "net7.0", - "dependencies": { - "Sylvan.Data.Csv": { - "target": "Package", - "version": "[1.2.3, )" - } - }, - "imports": [ - "net461", - "net462", - "net47", - "net471", - "net472", - "net48", - "net481" - ], - "assetTargetFallback": true, - "warn": true, - "frameworkReferences": { - "Microsoft.AspNetCore.App": { - "privateAssets": "none" - }, - "Microsoft.NETCore.App": { - "privateAssets": "all" - } - }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\7.0.100-rc.1.22431.12\\RuntimeIdentifierGraph.json" - } - } - } - } -} \ No newline at end of file diff --git a/Trannet/obj/Trannet.csproj.nuget.g.props b/Trannet/obj/Trannet.csproj.nuget.g.props deleted file mode 100644 index 0b5227b..0000000 --- a/Trannet/obj/Trannet.csproj.nuget.g.props +++ /dev/null @@ -1,16 +0,0 @@ - - - - True - NuGet - $(MSBuildThisFileDirectory)project.assets.json - $(UserProfile)\.nuget\packages\ - C:\Users\Tedd\.nuget\packages\;C:\Program Files (x86)\Microsoft Visual Studio\Shared\NuGetPackages - PackageReference - 6.3.0 - - - - - - \ No newline at end of file diff --git a/Trannet/obj/Trannet.csproj.nuget.g.targets b/Trannet/obj/Trannet.csproj.nuget.g.targets deleted file mode 100644 index 3dc06ef..0000000 --- a/Trannet/obj/Trannet.csproj.nuget.g.targets +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/Trannet/obj/project.assets.json b/Trannet/obj/project.assets.json deleted file mode 100644 index 56ea161..0000000 --- a/Trannet/obj/project.assets.json +++ /dev/null @@ -1,123 +0,0 @@ -{ - "version": 3, - "targets": { - "net7.0": { - "Sylvan.Data.Csv/1.2.3": { - "type": "package", - "compile": { - "lib/net6.0/Sylvan.Data.Csv.dll": { - "related": ".xml" - } - }, - "runtime": { - "lib/net6.0/Sylvan.Data.Csv.dll": { - "related": ".xml" - } - } - } - } - }, - "libraries": { - "Sylvan.Data.Csv/1.2.3": { - "sha512": "XNpVvpoR6pcrfgOHaDCVLm1DBthhhi5VyOhUj+Nq7421JFqsvakQlcvQdB1swLW6PeBMC6Hst1VQuUXEQDEdEQ==", - "type": "package", - "path": "sylvan.data.csv/1.2.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "Sylvan.png", - "lib/net6.0/Sylvan.Data.Csv.dll", - "lib/net6.0/Sylvan.Data.Csv.xml", - "lib/netcoreapp3.0/Sylvan.Data.Csv.dll", - "lib/netcoreapp3.0/Sylvan.Data.Csv.xml", - "lib/netstandard2.0/Sylvan.Data.Csv.dll", - "lib/netstandard2.0/Sylvan.Data.Csv.xml", - "lib/netstandard2.1/Sylvan.Data.Csv.dll", - "lib/netstandard2.1/Sylvan.Data.Csv.xml", - "license.txt", - "readme.md", - "sylvan.data.csv.1.2.3.nupkg.sha512", - "sylvan.data.csv.nuspec" - ] - } - }, - "projectFileDependencyGroups": { - "net7.0": [ - "Sylvan.Data.Csv >= 1.2.3" - ] - }, - "packageFolders": { - "C:\\Users\\Tedd\\.nuget\\packages\\": {}, - "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages": {} - }, - "project": { - "version": "1.0.0", - "restore": { - "projectUniqueName": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj", - "projectName": "Trannet", - "projectPath": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj", - "packagesPath": "C:\\Users\\Tedd\\.nuget\\packages\\", - "outputPath": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\obj\\", - "projectStyle": "PackageReference", - "crossTargeting": true, - "fallbackFolders": [ - "C:\\Program Files (x86)\\Microsoft Visual Studio\\Shared\\NuGetPackages" - ], - "configFilePaths": [ - "C:\\Users\\Tedd\\AppData\\Roaming\\NuGet\\NuGet.Config", - "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.FallbackLocation.config", - "C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config" - ], - "originalTargetFrameworks": [ - "net7.0" - ], - "sources": { - "C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {}, - "C:\\Program Files\\dotnet\\library-packs": {}, - "https://api.nuget.org/v3/index.json": {} - }, - "frameworks": { - "net7.0": { - "targetAlias": "net7.0", - "projectReferences": {} - } - }, - "warningProperties": { - "warnAsError": [ - "NU1605" - ] - } - }, - "frameworks": { - "net7.0": { - "targetAlias": "net7.0", - "dependencies": { - "Sylvan.Data.Csv": { - "target": "Package", - "version": "[1.2.3, )" - } - }, - "imports": [ - "net461", - "net462", - "net47", - "net471", - "net472", - "net48", - "net481" - ], - "assetTargetFallback": true, - "warn": true, - "frameworkReferences": { - "Microsoft.AspNetCore.App": { - "privateAssets": "none" - }, - "Microsoft.NETCore.App": { - "privateAssets": "all" - } - }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\7.0.100-rc.1.22431.12\\RuntimeIdentifierGraph.json" - } - } - } -} \ No newline at end of file diff --git a/Trannet/obj/project.nuget.cache b/Trannet/obj/project.nuget.cache deleted file mode 100644 index 571381f..0000000 --- a/Trannet/obj/project.nuget.cache +++ /dev/null @@ -1,10 +0,0 @@ -{ - "version": 2, - "dgSpecHash": "+ji7et497GW3FErz4ITMfi3iUmwxjna/P8RLKLGwQ9GSayoiFkv3t+TH+hm+TH0w20s4ygcqcoITW07YQMhmkA==", - "success": true, - "projectFilePath": "D:\\SourceCode\\_PerformanceStuff\\transit-lang-cmp\\Trannet\\Trannet.csproj", - "expectedPackageFiles": [ - "C:\\Users\\Tedd\\.nuget\\packages\\sylvan.data.csv\\1.2.3\\sylvan.data.csv.1.2.3.nupkg.sha512" - ], - "logs": [] -} \ No newline at end of file diff --git a/Trannet/obj/staticwebassets.pack.sentinel b/Trannet/obj/staticwebassets.pack.sentinel deleted file mode 100644 index bca8fe0..0000000 --- a/Trannet/obj/staticwebassets.pack.sentinel +++ /dev/null @@ -1,12 +0,0 @@ -2.0 -2.0 -2.0 -2.0 -2.0 -2.0 -2.0 -2.0 -2.0 -2.0 -2.0 -2.0 From 311e51cc5279e2ed263aadd818d3699290727719 Mon Sep 17 00:00:00 2001 From: Tedd Hansen Date: Sun, 23 Oct 2022 22:37:38 +0200 Subject: [PATCH 5/6] TripResponse needs properties for default json behavior --- Trannet/Services/Models.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Trannet/Services/Models.cs b/Trannet/Services/Models.cs index 1d0738d..2744085 100644 --- a/Trannet/Services/Models.cs +++ b/Trannet/Services/Models.cs @@ -45,10 +45,10 @@ public Trip(string tripID, string routeID, string serviceID) public class TripResponse { - public readonly string trip_id; - public readonly string route_id; - public readonly string service_id; - public readonly List schedules; + public string trip_id { get; init; } + public string route_id { get; init; } + public string service_id { get; init; } + public List schedules { get; init; } public TripResponse(ref Trip trip, List stop_time_responses) { trip_id = trip.TripID; From a63c6511a1276a5599977a14ea6ddf82e82431d2 Mon Sep 17 00:00:00 2001 From: Tedd Hansen Date: Sun, 23 Oct 2022 22:44:15 +0200 Subject: [PATCH 6/6] Larger json buffer + include fields --- Trannet/Program.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Trannet/Program.cs b/Trannet/Program.cs index c862543..ca887d5 100644 --- a/Trannet/Program.cs +++ b/Trannet/Program.cs @@ -1,3 +1,5 @@ +using System.IO.Compression; +using Microsoft.AspNetCore.ResponseCompression; using Trannet.Services; // Load on startup, not on first request (would skew any benchmark). @@ -5,6 +7,14 @@ // Set up web api var builder = WebApplication.CreateBuilder(args); + +// Set JSON options for larger responses +builder.Services.AddControllers().AddJsonOptions(options => +{ + options.JsonSerializerOptions.IncludeFields = true; + options.JsonSerializerOptions.DefaultBufferSize = 100 * 4096; +}); + var app = builder.Build(); // Map URL directly to method with same signature