Optimierung der Korrekturmeldungsprechung für HochdurchsatzleistungC#

Ein Treffpunkt für C#-Programmierer
Anonymous
 Optimierung der Korrekturmeldungsprechung für Hochdurchsatzleistung

Post by Anonymous »

Ich verarbeite die Fix Meldungen mit hoher Intensität und muss die Eingabe -Fix -Nachrichtendaten effizient in ein Wörterbuch für die schnelle Suche umwandeln. Ich habe bereits einige Optimierungen versucht, z. B. die Verwendung von ReadOnlyspan anstelle von String , aber meine Benchmark-Ergebnisse sind immer noch unbefriedigend.

Code: Select all

public class FixDictionaryBase
{
private readonly Dictionary _dict;

protected FixDictionaryBase()
{
_dict = [];
}

public string GetTag(int tag) => _dict.GetValueOrDefault(tag);

protected void Parse(ReadOnlySpan inputSpan, out List groups)
{
// Algorithm for processing FIX message string:
// 1. Iterate through the input string and extract key-value pairs based on the splitter character.
// 2. If the key is RptSeq (83), initialize a new group and add it to the groups list.
// 3. Assign key-value pairs to the appropriate dictionary:
//    - If the key is 10 (Checksum), store it in the main _dict.
//    - If currently inside a group, store it in the dictionary of the current group.
//    - Otherwise, store it in the main _dict.
// 4.  Continue processing until no more splitter characters are found in the input string.

groups = [];

FixDictionaryBase currentGroup = null;

// Special characters used to separate data
const char splitter = '\x01';
const char equalChar = '=';
const int rptSeq = 83;

// Find the first occurrence of the splitter character
int splitterIndex = inputSpan.IndexOf(splitter);
var hasGroup = false;

while (splitterIndex != -1)
{
// Extract the part before the splitter to get the key-value pair
var leftPart = inputSpan[..splitterIndex];

// Find the position of '=' to separate key and value
var equalIndex = leftPart.IndexOf(equalChar);

// Extract key from the part before '='
var key = int.Parse(leftPart[..equalIndex]);

// Extract value from the part after '='
var value = leftPart[(equalIndex + 1)..].ToString();

// If the key is RptSeq (83), start a new group and add it to the groups list
if (key == rptSeq)
{
hasGroup = true;
currentGroup = new();
groups.Add(currentGroup);
}

// Determine the appropriate dictionary to store data
// - If the key is 10 (Checksum), always store it in the main _dict
// - If a group has been identified (hasGroup == true), store it in the current group's dictionary
// - Otherwise, store it in the main _dict
var isChecksum = key == 10;
var currentDict = isChecksum ? _dict : hasGroup ? currentGroup._dict : _dict;
currentDict[key] = value;

// Remove the processed part and continue searching for the next splitter
inputSpan = inputSpan[(splitterIndex + 1)..];
splitterIndex = inputSpan.IndexOf(splitter);
}
}
}

public sealed class FixDictionary : FixDictionaryBase
{
private readonly string _fixString;

public FixDictionary(string fixString) : base()
{
_fixString = fixString;
Parse(fixString, out var groups);
Groups = groups;
}

public IReadOnlyList Groups { get; }

public string GetFixString() =>  _fixString;
}
< /code>
 Benchmarkergebnisse < /h3>
Hier sind meine Benchmark-Ergebnisse mit Benchmarkdotnet: < /p>
BenchmarkDotNet v0.13.10, Windows 11 (10.0.26100.3476)
Intel Core i5-9400 CPU 2.90GHz (Coffee Lake), 1 CPU, 6 logical and 6 physical cores
.NET SDK 9.0.102
[Host]     : .NET 8.0.12 (8.0.1224.60305), X64 RyuJIT AVX2
DefaultJob : .NET 8.0.12 (8.0.1224.60305), X64 RyuJIT AVX2

| Method     | Mean     | Error     | StdDev    | Gen0   | Gen1   | Allocated |
|----------- |---------:|----------:|----------:|-------:|-------:|----------:|
| GetFixData | 8.044 μs | 0.0683 μs | 0.0639 μs | 3.5553 | 0.1678 |  16.38 KB |

< /code>
 Eingabebestand für Benchmark < /h3>
8=FIX.4.4[SOH]9=1087[SOH]35=X[SOH]49=VNMGW[SOH]56=99999[SOH]34=433615[SOH]52=20250325 06:00:02.273[SOH]30001=UPX[SOH]20004=G1[SOH]336=99[SOH]55=VN000000YTC0[SOH]75=20250311[SOH]60=060000346[SOH]30522=200[SOH]30524=2[SOH]268=20[SOH]83=1[SOH]279=0[SOH]269=1[SOH]290=1[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=2[SOH]279=0[SOH]269=0[SOH]290=1[SOH]270=33000.0[SOH]271=100[SOH]346=1[SOH]30271=0[SOH]83=3[SOH]279=0[SOH]269=1[SOH]290=2[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=4[SOH]279=0[SOH]269=0[SOH]290=2[SOH]270=31800.0[SOH]271=100[SOH]346=1[SOH]30271=0[SOH]83=5[SOH]279=0[SOH]269=1[SOH]290=3[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=6[SOH]279=0[SOH]269=0[SOH]290=3[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=7[SOH]279=0[SOH]269=1[SOH]290=4[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=8[SOH]279=0[SOH]269=0[SOH]290=4[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=9[SOH]279=0[SOH]269=1[SOH]290=5[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=10[SOH]279=0[SOH]269=0[SOH]290=5[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=11[SOH]279=0[SOH]269=1[SOH]290=6[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=12[SOH]279=0[SOH]269=0[SOH]290=6[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=13[SOH]279=0[SOH]269=1[SOH]290=7[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=14[SOH]279=0[SOH]269=0[SOH]290=7[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=15[SOH]279=0[SOH]269=1[SOH]290=8[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=16[SOH]279=0[SOH]269=0[SOH]290=8[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=17[SOH]279=0[SOH]269=1[SOH]290=9[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=18[SOH]279=0[SOH]269=0[SOH]290=9[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=19[SOH]279=0[SOH]269=1[SOH]290=10[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]83=20[SOH]279=0[SOH]269=0[SOH]290=10[SOH]270=0.0[SOH]271=0[SOH]30271=0[SOH]10=103[SOH]
< /code>
mit [SOH] ist '\ x01' < /p>
 Ziel < /H3>
Ich ziele darauf ab, mindestens 3 × die aktuelle Geschwindigkeit zu erreichen, während die Gedächtniszuteilung minimal bleibt. Leistung und niedrigere Speicherzuweisung? SIMD, Span  
-basierte Analyse oder andere Optimierungen mit niedrigem Niveau in diesem Fall von Vorteil? geschätzt
Bearbeiten
Ich habe auch ein Bild meiner Fix -Zeichenfolge angehängt, um Klarheit zu gewährleisten:

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post