Skip to content

Latest commit

 

History

History
329 lines (250 loc) · 13.1 KB

README.md

File metadata and controls

329 lines (250 loc) · 13.1 KB

Velo

.NET Core codecov CodeFactor NuGet NuGet

Do not use in production environment. It's just a training project.

The library is performance-oriented package of popular patterns and workflows:

  • CQRS,
  • IoC,
  • serialization/deserialization,
  • logging and mapping

Install from nuget

Install Velo with the following command from nuget:

Install-Package Velo

For integration with IServiceCollection, install Velo.Extensions.DependencyInjection from nuget:

Install-Package Velo.Extensions.DependencyInjection

Emitter (mediator)

var dependencyProvider = new DependencyCollection()
    .AddEmitter()            // mediator infrastructure
    .Scan(scanner => scanner // collect all processors and behaviours
        .AssemblyOf<IBooRepository>()
        .RegisterEmitterProcessors())     
    .BuildProvider();

var emitter = dependencyProvider.GetRequired<Emitter>();

// ask query (send request)
Boo boo = await emitter.Ask(new GetBoo { Id = id }); 

// execute command
await emitter.Execute(new CreateBoo { Id = id }); 

// publish notification
await emitter.Publish(new Notification { Created = true });

// or ask as struct for reduce memory traffic
Boo boo = await emitter.Ask<GetBooStruct, Boo>(new GetBooStruct {Id = id}); 

Registration

var dependencyProvider = new DependencyCollection()
    .AddEmitter()                            // mediator infrastructure
    .AddCommandBehaviour<MeasureBehaviour>() // add behaviours
    .AddCommandProcessor<PreProcessor>()
    .AddCommandProcessor<Processor>(DependencyLifetime.Scoped) 
    .AddCommandProcessor<PostProcessor>()
    .AddQueryProcessor<QueryPreProcessor>()
    .AddQueryProcessor<QueryProcessor>()
    .AddQueryProcessor<QueryPostProcessor>()
    .AddNotificationProcessor<OnBooCreated>()
    .BuildProvider();

Mediator query (request) benchmark (per 1000 requests)

Method Mean Error StdDev Ratio Allocated
FullPipeline_MediatR 805,866.1 ns 15,881.23 ns 21,738.41 ns 1.00 1056.07 KB
FullPipeline_Velo 272,772.9 ns 5,236.39 ns 6,430.75 ns 0.34 256.07 KB
One_Request_MediatR 358,579.4 ns 6,727.49 ns 7,198.33 ns 1.00 376.07 KB
One_Request_Velo 132,259.3 ns 2,136.00 ns 1,998.02 ns 0.37 144.07 KB

FullPipeline - behaviour, pre- and post-processor.

Mediator notification benchmark (per 1000 notifications)

Method Mean Error StdDev Ratio Allocated
MediatR 469.6 us 9.69 us 8.59 us 1.00 776 074 B
Velo 107.4 us 1.63 us 1.44 us 0.23 72 B

Mapper

var compiledMapper = new CompiledMapper<Foo>();

var source = new Boo
{
    Bool = true,
    Float = 1f,
    Int = 11
};

var foo = compiledMapper.Map(source);

Benchmark (per 10000 objects)

Method Mean Error StdDev Ratio Allocated
AutoMapper 998.9 us 10.17 us 9.51 us 1.00 390.63 KB
Velo 299.7 us 3.06 us 2.86 us 0.30 390.63 KB

Serialization/Deserialization

Deserialization

var dependencyProvider = new DependencyCollection()
    .AddJson() // json converter infrastructure
    .BuildProvider();

var converter = dependencyProvider.GetRequired<JConverter>();
var deserialized = converter.Deserialize<Boo[]>(json);

Serialization

var converter = dependencyProvider.GetRequired<JConverter>();
var json = converter.Serialize(data);

Serialization benchmark (per 10000 objects)

Method Mean Error StdDev Ratio Allocated
Newtonsoft 54.83 ms 0.567 ms 0.531 ms 1.00 32.71 MB
Velo 28.71 ms 0.431 ms 0.404 ms 0.52 9.87 MB

Deserialization benchmark (per 10000 objects)

Method Mean Error StdDev Ratio Allocated
Newtonsoft 88.41 ms 1.575 ms 1.315 ms 1.00 36.85 MB
Velo 44.28 ms 0.327 ms 0.306 ms 0.50 19.26 MB

Logger

Configure logger

var dependencyProvider = new DependencyCollection()
    .AddLogger()
    .AddDefaultLogEnrichers()         // log level, sender, timestamp
    .AddLogEnricher<Enricher>()       // add your enricher 
    .AddDefaultConsoleLogWriter()     // primitive log writer
    .AddLogWriter<LogWriter>()        // add your writer
    .BuildProvider();

Use logger

var logger = dependencyProvider.GetRequired<ILogger<MyClass>>();
logger.Debug("My code for handling {instance} executed at {elapsed}", instance, timer.Elapsed);

Structured logging

[DBG] [MyClass] [2020-02-26T17:07:57] My code for handling { "Id": 129, "Bool": true, "Double": 61, "Float": 198, "Int": 11, "IntNullable": 177, "String": "String64630110-c7c9-4ba2-94c0-4c28dd9cea20", "Type": 0, "Values": [36,17,212] } executed at "0:00:00.0000025".

Logger benchmark (per 1000 log events)

Method Mean Error StdDev Ratio Allocated
Serilog_EmptySink 1,110.0 us 21.86 us 23.39 us 1.00 851.56 KB
Nlog_EmptyTarget 2,355.9 us 44.31 us 47.41 us 2.12 1 911.15 KB
Velo_EmptyWriter 902.4 us 12.68 us 11.86 us 0.82 219.25 KB
Serilog_StringWriter 1,439.7 us 14.88 us 12.42 us 1.00 976.25 KB
Nlog_StringWriter 2,061.0 us 26.41 us 24.70 us 1.43 1 648.44 KB
Velo_StringWriter 1,225.1 us 11.20 us 9.93 us 0.85 219.56 KB

Dependency Injection

Create dependency provider

var dependencyProvider = new DependencyCollection()
    .AddScoped<SomethingController>()
    .AddSingleton<IFooService, FooService>()
    .AddSingleton(typof(IMapper<>), typeof(CompiledMapper<>))
    .AddSingleton<IConfiguration>(ctx => new Configuration())
    .AddTransient<ISession, Session>()
    .BuildProvider();

Use an assembly scanner to find generic interface implementations

var dependencyProvider = new DependencyCollection()
    .Scan(scanner => scanner
        .AssemblyOf<IRepository>()
        .RegisterAsSingleton(typeof(IRepository<>)))
    .BuildProvider();

Resolve dependency

// possible null or empty
var repositoryArray = dependencyProvider.Get<IRepository[]>();

// not null or exception
var converterSingleton = dependencyProvider.GetRequired<JConverter>();

// registered as transient
var session = dependencyProvider.Get<ISession>();
var otherSession = dependencyProvider.Get<ISession>();

Use scope

using (var scope = dependencyProvider.StartScope())
{
    var controller = scope.Get<SomethingController>();
}

Benchmarks

Create dependency container benchmark

Method Mean Error StdDev Ratio Allocated
Autofac 42.710 us 0.4666 us 0.4136 us 18.28 38.9 KB
Castle 245.270 us 2.0700 us 1.9363 us 105.06 91.71 KB
Core 2.338 us 0.0254 us 0.0212 us 1.00 5.54 KB
LightInject 13.078 us 0.0809 us 0.0757 us 5.60 37.45 KB
SimpleInject 401.719 us 3.0091 us 2.6675 us 171.80 42.7 KB
Velo 1.739 us 0.0200 us 0.0178 us 0.74 3.04 KB
Unity 15.096 us 0.2847 us 0.2796 us 6.44 22.41 KB

Resolve singleton service from container

Method Mean Error StdDev Ratio Allocated
Autofac 762.68 ns 15.218 ns 25.841 ns 3.83 1 656 B
Castle 536.30 ns 5.416 ns 5.066 ns 2.71 1 200 B
Core 198.20 ns 2.320 ns 2.057 ns 1.00 216 B
LightInject 85.05 ns 0.624 ns 0.584 ns 0.43 216 B
SimpleInject 128.19 ns 0.942 ns 0.881 ns 0.65 216 B
Velo 195.92 ns 2.089 ns 1.954 ns 0.99 216 B
Unity 455.58 ns 3.135 ns 2.932 ns 2.30 552 B

LocalVector (small collection on stack)

Ref struct to collect values on stack. This collection allows you to reduce memory consumption. Also, it allows to work with several variables as a collection without extra costs. Read about LocalList here.

Usage

var vector = new LocalVector<Boo>();
vector.Add(new Boo()); // add less 10 elements for performance effect

Linq-like via ref struct enumerators

var outer = new LocalVector<Boo>(_items);
var inner = new LocalVector<Boo>(_reversItems);

var counter = 0;
foreach (var number in outer
    .Join(inner, o => o, i => i, (o, i) => i) 
    .Where((b, threshold) => b.Int > threshold, _threshold) // use an argument to avoid closure
    .Select((b, modifier) => b.Id * modifier, _modifier)
    .OrderBy(id => id))
{
    counter += number;
}

return counter;

Local vector benchmarks (collection with 10 elements)

Method Mean Error StdDev Ratio Allocated
List_Add 12.926 ns 0.1850 ns 0.1731 ns 1.00 33 B
LocalVector_Add 5.502 ns 0.0313 ns 0.0244 ns 0.42 -
Span_Add 3.852 ns 0.0196 ns 0.0153 ns 0.30 10 B
List_Iteration 9.937 ns 0.2311 ns 0.2923 ns 1.00 14 B
LocalVector_Iteration 9.701 ns 0.0504 ns 0.0472 ns 0.97 -
Span_Iteration 8.521 ns 0.0899 ns 0.0797 ns 0.85 10 B
List_GroupBy 71.959 ns 0.5214 ns 0.4877 ns 1.00 102 B
LocalVector_GroupBy 83.630 ns 0.2430 ns 0.2273 ns 1.16 -
List_Join 111.531 ns 0.3870 ns 0.3232 ns 1.00 167 B
LocalVector_Join 117.925 ns 0.3964 ns 0.3708 ns 1.06 -
List_ManyLinq 238.136 ns 1.0246 ns 0.9584 ns 1.00 366 B
LocalVector_ManyLinq 425.516 ns 1.3847 ns 1.2952 ns 1.79 -
List_Remove 31.604 ns 0.2091 ns 0.1956 ns 1.00 14 B
LocalVector_Remove 52.423 ns 0.3658 ns 0.3422 ns 1.66 -
List_Select 22.865 ns 0.0749 ns 0.0664 ns 1.00 21 B
LocalVector_Select 17.396 ns 0.0883 ns 0.0737 ns 0.76 -
List_ToArray 8.174 ns 0.0553 ns 0.0461 ns 1.00 24 B
LocalVector_ToArray 11.211 ns 0.0623 ns 0.0583 ns 1.37 10 B
Span_ToArray 2.284 ns 0.0195 ns 0.0182 ns 0.28 10 B
List_Where 22.720 ns 0.0935 ns 0.0874 ns 1.00 21 B
List_FindAll 20.528 ns 0.1968 ns 0.1744 ns 0.90 31 B
LocalVector_Where 18.991 ns 0.0860 ns 0.0805 ns 0.84 -