1
0
mirror of https://github.com/JKorf/CryptoExchange.Net.git synced 2026-10-16 08:01:06 +00:00

Websocket performance update (#261)

Performance update:

Authentication
	Added Ed25519 signing support for NET8.0 and newer
	Added static methods on ApiCredentials to create credentials of a specific type
	Added static ApiCredentials.ReadFromFile method to read a key from file
	Added required abstract SupportedCredentialTypes property on AuthenticationProvider base class

General Performance
	Added checks before logging statements to prevent overhead of building the log string if logging is not needed	
	Added ExchangeHelpers.ProcessQueuedAsync method to process updates async
	Replaced locking object types from object to Lock in NET9.0 and newer 
	Replaced some Task response types with ValueTask to prevent allocation overhead on hot paths
	Updated Json ArrayConverter to reduce some allocation overhead 
	Updated Json BoolConverter to prevent boxing
	Updated Json DateTimeConverter to prevent boxing
	Updated Json EnumConverter caching to reduce lookup overhead
	Updated ExtensionMethods.CreateParamString to reduce allocations
	Updated ExtensionMethods.AppendPath to reduce overhead	

REST 
	Refactored REST message processing to separate IRestMessageHandler instance
	Split RestApiClient.PrepareAsync into CheckTimeSync and RateLimitAsync
	Updated IRequest.Accept type from string to MediaTypeWithQualityHeaderValue to prevent creation on each request
	Updated IRequest.GetHeaders response type from KeyValuePair<string, string[]>[] to HttpRequestHeaders to prevent additional mapping
	Updated IResponse.ResponseHeaders type from KeyValuePair<string, string[]>[] to HttpResponseHeaders to prevent additional mapping
	Updated WebCallResult RequestHeaders and ResponseHeaders types to HttpRequestHeaders and HttpResponseHeaders	
	Removed unnecessary empty dictionary initializations for each request
	Removed CallResult creation in internal methods to prevent having to create multiple versions for different result types 

Socket
	Added HighPerformance websocket client implementation which significantly reduces memory overhead and improves speed but with certain limitations
	Added MaxIndividualSubscriptionsPerConnection setting in SocketApiClient to limit the number of individual stream subscriptions on a connection
	Added SocketIndividualSubscriptionCombineTarget option to set the target number of individual stream subscriptions per connection
	Added new websocket message handling logic which is faster and reduces memory allocation
	Added UseUpdatedDeserialization option to toggle between updated deserialization and old deserialization 
	Added Exchange property to DataEvent to prevent additional mapping overhead for Shared apis
	Refactored message callback to be sync instead of async to prevent async overhead
	Refactored CryptoExchangeWebSocketClient.IncomingKbps calculation to significantly reduce overhead
	Moved websocket client creation from SocketApiClient to SocketConnection	
	Removed DataEvent.As and DataEvent.ToCallResult methods in favor of single ToType method
	Removed DataEvent creation on lower levels to prevent having to create multiple versions for different result types
	Removed Subscription<TSubResponse, TUnsubResponse> as its no longer used

Other
	Added null check to ParameterCollection for required parameters 
	Added Net10.0 target framework
	Updated dependency versions
	Updated Shared asset aliases check to be culture invariant
	Updated Error string representation
	Updated some namespaces
	Updated SymbolOrderBook processing of buffered updates to prevent additional allocation
	Removed ExchangeEvent type which is no longer needed
	Removed unused usings
This commit is contained in:
Jan Korf
2025-12-16 11:27:49 +01:00
committed by GitHub
parent f125bc88b0
commit d079796020
238 changed files with 5061 additions and 2066 deletions
@@ -1,34 +0,0 @@
using CryptoExchange.Net.Objects.Sockets;
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// An update event for a specific exchange
/// </summary>
/// <typeparam name="T">Type of the data</typeparam>
public class ExchangeEvent<T> : DataEvent<T>
{
/// <summary>
/// The exchange
/// </summary>
public string Exchange { get; }
/// <summary>
/// ctor
/// </summary>
public ExchangeEvent(string exchange, DataEvent<T> evnt) :
base(evnt.Data,
evnt.StreamId,
evnt.Symbol,
evnt.OriginalData,
evnt.ReceiveTime,
evnt.UpdateType)
{
DataTime = evnt.DataTime;
Exchange = exchange;
}
/// <inheritdoc />
public override string ToString() => $"{Exchange} - " + base.ToString();
}
}
@@ -39,8 +39,8 @@ namespace CryptoExchange.Net.SharedApis
/// <returns></returns>
public bool HasValue(string exchange, string name, Type type)
{
var val = _parameters.SingleOrDefault(x => x.Exchange == exchange && x.Name == name);
val ??= _staticParameters.SingleOrDefault(x => x.Exchange == exchange && x.Name == name);
var val = _parameters.SingleOrDefault(x => x.Exchange.Equals(exchange, StringComparison.InvariantCulture) && x.Name.Equals(name, StringComparison.InvariantCulture));
val ??= _staticParameters.SingleOrDefault(x => x.Exchange.Equals(exchange, StringComparison.InvariantCulture) && x.Name.Equals(name, StringComparison.InvariantCulture));
if (val == null)
return false;
@@ -71,7 +71,7 @@ namespace CryptoExchange.Net.SharedApis
if (provided == true)
return true;
var val = _staticParameters.SingleOrDefault(x => x.Exchange == exchange && x.Name == name);
var val = _staticParameters.SingleOrDefault(x => x.Exchange.Equals(exchange, StringComparison.InvariantCulture) && x.Name.Equals(name, StringComparison.InvariantCulture));
if (val == null)
return false;
@@ -95,7 +95,7 @@ namespace CryptoExchange.Net.SharedApis
/// <param name="name">Parameter name</param>
public T? GetValue<T>(string exchange, string name)
{
var val = _parameters.SingleOrDefault(x => x.Exchange == exchange && x.Name == name);
var val = _parameters.SingleOrDefault(x => x.Exchange.Equals(exchange, StringComparison.InvariantCulture) && x.Name.Equals(name, StringComparison.InvariantCulture));
if (val == null)
return default;
@@ -122,7 +122,7 @@ namespace CryptoExchange.Net.SharedApis
T? value;
if (exchangeParameters == null)
{
var parameter = _staticParameters.SingleOrDefault(x => x.Exchange == exchange && x.Name == name);
var parameter = _staticParameters.SingleOrDefault(x => x.Exchange.Equals(exchange, StringComparison.InvariantCulture) && x.Name.Equals(name, StringComparison.InvariantCulture));
if (parameter == null)
return default;
@@ -155,7 +155,7 @@ namespace CryptoExchange.Net.SharedApis
/// <param name="value">Parameter value</param>
public static void SetStaticParameter(string exchange, string key, object value)
{
var existing = _staticParameters.SingleOrDefault(x => x.Exchange == exchange && x.Name == key);
var existing = _staticParameters.SingleOrDefault(x => x.Exchange.Equals(exchange, StringComparison.InvariantCulture) && x.Name.Equals(key, StringComparison.InvariantCulture));
if (existing != null)
{
existing.Value = value;
@@ -1,9 +1,9 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
namespace CryptoExchange.Net.SharedApis
{
@@ -103,7 +103,7 @@ namespace CryptoExchange.Net.SharedApis
TradingMode[]? dataTradeModes,
HttpStatusCode? code,
Version? httpVersion,
KeyValuePair<string, string[]>[]? responseHeaders,
HttpResponseHeaders? responseHeaders,
TimeSpan? responseTime,
long? responseLength,
string? originalData,
@@ -111,7 +111,7 @@ namespace CryptoExchange.Net.SharedApis
string? requestUrl,
string? requestBody,
HttpMethod? requestMethod,
KeyValuePair<string, string[]>[]? requestHeaders,
HttpRequestHeaders? requestHeaders,
ResultDataSource dataSource,
[AllowNull] T data,
Error? error,
@@ -1,7 +1,6 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Text;
@@ -1,6 +1,5 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -1,6 +1,5 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
@@ -1,8 +1,4 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
@@ -1,8 +1,4 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text;
namespace CryptoExchange.Net.SharedApis
{
@@ -1,5 +1,4 @@
using CryptoExchange.Net.Objects;
using System.Diagnostics.CodeAnalysis;
using System.Diagnostics.CodeAnalysis;
using System.Text;
namespace CryptoExchange.Net.SharedApis
@@ -1,6 +1,5 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
@@ -1,7 +1,4 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
@@ -1,6 +1,5 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
@@ -1,7 +1,4 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
{
@@ -1,6 +1,5 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
@@ -1,6 +1,5 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
@@ -1,9 +1,4 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for subscribing to ticker updates
@@ -1,9 +1,4 @@
using CryptoExchange.Net.Objects;
using System;
using System.Collections.Generic;
using System.Linq;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Options for subscribing to ticker updates
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve a list of supported assets
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve balance info for the user
@@ -1,8 +1,4 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve trading fees
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve the current open orders
@@ -1,5 +1,4 @@
using CryptoExchange.Net.Objects;
using System;
using System;
namespace CryptoExchange.Net.SharedApis
{
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve the current position mode
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve open positions
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve symbol info
@@ -1,7 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to retrieve all symbol tickers
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to keep-alive the update stream for the specified listen key
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to change the current position mode
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to start the update stream for the current user
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to stop the update stream for the specific listen key
@@ -1,5 +1,4 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to subscribe to ticker updates for all symbols
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to subscribe to balance updates
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to subscribe to futures order updates
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to subscribe to position updates
@@ -1,5 +1,4 @@
using System.Collections;
using System.Collections.Generic;
using System.Collections.Generic;
namespace CryptoExchange.Net.SharedApis
{
@@ -1,6 +1,4 @@
using CryptoExchange.Net.Objects;
namespace CryptoExchange.Net.SharedApis
namespace CryptoExchange.Net.SharedApis
{
/// <summary>
/// Request to subscribe to user trade updates