Updated Logging Packages available

by Dennis 19. February 2021 09:56

We’ve just uploaded new versions of all SphinxConnector.NET logging packages that contain a fix for a dependency issue. Due to an error in the build pipeline these packages were build while the version of SphinxConnector.NET was still at 4.4.0.



SphinxConnector.NET 5.0 has been released

by Dennis 9. February 2021 17:03

We're pleased to announce that SphinxConnector.NET 5.0 is now available via NuGet! You can also download a build from our site, but that only contains a version for .NET Framework 4.6.1. and higher.

With this release we’re saying good bye to Common.Logging, which has been replaced with our own logging abstraction. We’re providing separate NuGet packages for the most commonly used logging frameworks, namely:

Also included in SphinxConnector.NET is a simple console logging provider that can be used during development.

The second change in dependencies is the switch from JSON.NET to System.Text.Json as the default json handler. If for some reason you need to stay on JSON.NET, you can easily do so by implementing your own JsonObjectSerializer.

SphinxConnector.NET now includes builds for .NET Core 3.1 and .NET 5.0, where we’ve added implementations for new async methods to SphinxQLDataReader that were introduced with .NET Core 3.1. These builds also contain fluent API support for async enumerables via IFulltextQuery.ToAsyncEnumerable().

The biggest new feature is the introduction of prepared (sometimes also called ‘compiled’) queries to the fluent API.
The idea behind prepared queries is to create a query template that is parsed once and then reused on the following executions thus alleviating the overhead caused by parsing the expression tree and generating the SphinxQL statement. This can lead to significant reductions in allocations and time spend on parsing the expression tree of a query, especially in high load scenarios, while at the same time giving you all the advantages of a strongly typed query API. More about prepared queries can be found in the documentation.

For a full list of changes, please refer to the version history.


Announcements | SphinxConnector.NET

SphinxConnector.NET 4.3 has been released

by Dennis 13. December 2019 16:00

We're pleased to announce that SphinxConnector.NET 4.3 is available for download and via NuGet!

In this release we've added a new BulkCopy class which efficiently copies data from an IDataReader to an index. We've also added support for the REMAP() function to the fluent API.

For a full list of changes, please refer to the version history.


Announcements | SphinxConnector.NET

Comparing JSON Serializers

by Dennis 2. May 2019 13:44

As you might be aware, SphinxConnector.NET’s fluent API uses JSON.NET as its default JSON serializer when dealing with JSON attributes. As the old saying goes “No one has ever been fired for using JSON.NET” ;-) Choosing JSON.NET was obvious, because it has been the de-facto standard in the .NET world since, well, forever. Even Microsoft adopted it as the default library for handling JSON in ASP.NET (MVC/Web API), so it’s probably not even an additional dependency for most people, which is a bonus.

There are however a bunch of other JSON libraries out there that do better in terms of performance and memory usage than JSON.NET, so I decided to test some them with real world data of a customer. I’ll be comparing

to JSON.NET 12.0.1.

Switching JSON serializers in SphinxConnector.NET’s fluent API is pretty easy, just inherit from JsonObjectSerializer, implement Serialize and Deserialize, register your class by setting the JsonObjectSerializer property and you are good to go. Here’s how the ServiceStackJsonSerializer looks like:

class ServiceStackJsonSerializer : JsonObjectSerializer
    public override string Serialize(object obj) => ServiceStack.Text.JsonSerializer.SerializeToString(obj);

    public override object Deserialize(string json, Type type) => ServiceStack.Text.JsonSerializer.DeserializeFromString(json, type);

As mentioned earlier, I’m using real world data that is used in production and is pretty JSON heavy. For the benchmark I’m just querying the JSON data, to better isolate the effect of using the different libraries with regards to execution speed (Op/s). Here are the results (data queried from Manticore 2.8.2):

BenchmarkDotNet=v0.11.5, OS=Windows 7 SP1 (6.1.7601.0)
Intel Core i5-3570 CPU 3.40GHz (Ivy Bridge), 1 CPU, 4 logical and 4 physical cores
Frequency=3420566 Hz, Resolution=292.3493 ns, Timer=TSC
  [Host]     : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3324.0
  Job-FEFVFA : .NET Framework 4.7.2 (CLR 4.0.30319.42000), 64bit RyuJIT-v4.7.3324.0
InvocationCount=1  UnrollFactor=1  
Method Mean Error StdDev Op/s Ratio Gen 0 Gen 1 Gen 2 Allocated
JsonNet 205.2 ms 4.617 ms 7.456 ms 4.874 1.00 3000.0 1000.0 - 13.75 MB
ServiceStack.Text 195.9 ms 1.734 ms 1.537 ms 5.104 0.94 1000.0 - - 7.24 MB
Utf8Json 190.9 ms 1.335 ms 1.249 ms 5.239 0.92 2000.0 - - 9.45 MB
JilJson 190.0 ms 1.263 ms 1.182 ms 5.262 0.92 3000.0 1000.0 - 14.44 MB

The results are pretty impressive: Servicestack.Text is using nearly 50% less memory, causes less garbage collections and is also slightly faster than JSON.NET*. Utf8Json is even faster than Servicestack.Text but uses more memory. Jil performs best, albeit slightly, but does so at the cost of an even higher memory consumption than JSON.NET, which isn’t surprising as this an intentional design decision.

Could we do even better?

We can. In SphinxConnector.NET 4.2 we'll be adding two more overloads to the Deserialize method:

public virtual object Deserialize(ReadOnlyMemory<byte> json, Type type)
public virtual object Deserialize(ReadOnlyMemory<char> json, Type type)

Instead of passing a string containing the JSON data, we are passing an instance of ReadOnlyMemory of char or byte. This way, we’re avoiding the creation of an intermediate string and instead pass on the raw data we’ve read from the socket.

ServiceStack.Text supports deserializing JSON from ReadOnlyMemory<char>, Utf8Json accepts an array of bytes. Jil and JSON.NET don’t offer any methods to that work directly on chars or bytes, here we need to create a MemoryStream over ReadOnlyMemory<byte> which in turn gets passed into a TextReader which can then be passed to the corresponding Deserialize methods.

Method Mean Error StdDev Op/s Ratio Gen 0 Gen 1 Gen 2 Allocated
JsonNet 196.9 ms 2.050 ms 1.8170 ms 5.078 1.00 3000.0 1000.0 - 13.25 MB
ServiceStack.Text 191.3 ms 1.119 ms 0.9346 ms 5.228 0.97 1000.0 - - 5.56 MB
Utf8Json 189.4 ms 1.088 ms 1.0177 ms 5.279 0.96 2000.0 - - 7.75 MB
Jil 193.2 ms 1.295 ms 1.2116 ms 5.175 0.98 3000.0 1000.0 - 13.93 MB

After eliminating the intermediate string allocations and passing our chars/bytes directly to ServiceStack.Text / Utf8Json we can observe a further reduction in memory usage. JSON.NET and Jil see some improvement, but not as much as the other two. JSON.NET also is faster than before in terms of Op/s whereas Jil is slightly slower.

* The performance advantages of the tested libraries compared to JSON.NET don’t show that much in a benchmark like this, because there’s much more going on here than just deserializing JSON data (query execution, network transmission etc.). If you’d just compare the deserialization process, you’d see much more of a speed advantage these libraries provide (check out the benchmarks on the respective project sites).



Replacing JSON.NET with an alternative JSON serializer in SphinxConnector.NET’s fluent API can lead to a significant reduction in memory usage and a nice improvement in speed. Given that this can be done with just a few lines of code, this is something I’d seriously look into for applications that make use of JSON attributes. On a related note, I’d also consider replacing JSON.NET within ASP.NET MVC/Web API in case your application is build with these.

Tags: ,


SphinxConnector.NET 4.1 has been released

by Dennis 30. November 2018 15:13

We're pleased to announce that SphinxConnector.NET 4.1 is available for download and via NuGet!

For this release we’ve continued our work on improving performance and reducing memory usage. With the release of .NET Core 2.1 and the introduction of Span and related types, we were able to further improve the work done in 4.0. For example, we are now able to read data from searchd for primitive types more efficiently, namely by reading and converting data directly from the array of bytes instead of creating a string first. We also optimized the process of building JSON path strings within the fluent API, which could be a big source for allocations.

For a full list of changes, please refer to the version history.