Laden...

Speicherverbrauchs Ideen für C# Applikation

Erstellt von jogibear9988 vor einem Jahr Letzter Beitrag vor einem Jahr 451 Views
J
jogibear9988 Themenstarter:in
641 Beiträge seit 2007
vor einem Jahr
Speicherverbrauchs Ideen für C# Applikation

Hallo,

wir haben eine Applikation, ein Visualisierungssystem für PLCs.

Aus Performance Gründen halten wir immer alle aktuellen werte als Klasse im Speicher. Dadurch das wir Visualiserungen haben mit mehr als 1,5 Mio Variablen, haben wir einen ganz schönen Speicherverbrauch.
da ja jede Variable durch eine Klasseninstanz repräsentiert wird.

Nun bin ich am überlgen das ganze etwas zu reduzieren, dazu ein paar fragen:

  • Was braucht ein leeres Feld vom Type "object" einer Klasse im Speicher? Ich würde im Moment mal auf 8 Byte tippen, oder täusche ich mich da?
  • Was braucht ein Bool? Würdet Ihr es als sinnvoll erachten mehrere Bool's in eine Flags enum zu verstecken (nur zur speicheroptimierung?)

cSharp Projekte : https://github.com/jogibear9988

16.806 Beiträge seit 2008
vor einem Jahr
  • Was braucht ein leeres Feld vom Type "object" einer Klasse im Speicher? Ich würde im Moment mal auf 8 Byte tippen, oder täusche ich mich da?

4 Bytes unter x86 und 8 Bytes unter x64

  • Was braucht ein Bool?

1 Byte, aber das Alignment führt zu 4 Bytes unter x86 und 8 Bytes unter x64.

Würdet Ihr es als sinnvoll erachten mehrere Bool's in eine Flags enum zu verstecken (nur zur speicheroptimierung?)

Solange die Korrektheit erfüllt wird, sind solche Dinge okay; Korrektheit darf halt nie leiden.

Für mich hört sich das Vorgehen aber nach einem wilden Rumgestochere an, was die Memory Verbesseerung betrifft.
Ich hab auch schon viele PLC Visualisierungen gesehen (und mit optimiert): immer lag es an der Software Architektur, die halt kacke war und dessen Auswirkungen unnötig Speicher gefressen hat.
Geschweige denn, dass man Basics von .NET Memory Management einfach nicht kannte.

J
jogibear9988 Themenstarter:in
641 Beiträge seit 2007
vor einem Jahr

Ein "Tag" braucht bei uns ca. 500 Bytes als Leeres Objekt (durch die Verschiedenen Properties die wir im Tag haben (Min/Max Value, Scripte, ...)). Das ist aber noch ohne Strings wie dem Tag Namen und der Adresse. D.h. wenn ich 1.5mio tags habe, welche im speicher sind, dann habe ich alleine dafür knapp 1GB speicher, da ich aber noch strings wie Name, Adresse und Beschreibung habe, komme ich auf ca 1.5GB.

Das ganze funktioniert ja so auch, ich überlege einfach nur was, und ob ich es reduzieren kann oder soll.

Ein Tag hat bei uns z.b. über 30 Properties wo man ihn konfigurieren kann, wie archivierung, meldung erzeugen, usw...

cSharp Projekte : https://github.com/jogibear9988

J
jogibear9988 Themenstarter:in
641 Beiträge seit 2007
vor einem Jahr

Hab den code genutzt um die leere objekt größe zu ermitteln: https://www.codeproject.com/Articles/1254217/Calculating-Heap-Size-of-Managed-Objects

cSharp Projekte : https://github.com/jogibear9988

16.806 Beiträge seit 2008
vor einem Jahr

Korrekt, Basics: im Speicher hält man das, was man zwingend braucht.
Man schiebt auch nicht Dinge in die UI, die mal nicht sehen kann - zB braucht niemand eine UI Liste mit 1.5 Einträgen (als Beispiel).

Zur Größenmessung: beachte, dass Du schnell in Messfehler rutschen kannst.
Die Runtime-Größe muss nicht der Theorie entsprechen.

Beispiel: jeder Referenztyp hat mindestens 12 Bytes, die tatsächlich benötigt werden, auch wenn das Objekt nur mind. 4 Byte hat.
Hinzu kommt nämlich noch die Verwaltung des Objekts; 4 Bytes Object Header und 4 Bytes Table Pointer.
Value Types haben das nicht.

In Visual Studio gibts ein .NET Object Allocation Tracking Tool, das dir die gesamte Größe anzeigt.

4.931 Beiträge seit 2008
vor einem Jahr

Bei Wertetypen wird direkt der Wert gespeichert, bei Referenztypen wird für die Referenz (intern ein Zeiger) selber Speicher je nach Bittigkeit des Prozesses (also 32 oder 64 bit) benötigt als auch das eigentliche Objekt (+Verwaltungsdaten).
Kannst es selber mal ausprobieren anhand folgenden Codes:


using System;
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public class C
{
}

public struct S
{
}

public class Test
{
	public static void Main()
	{
		Console.WriteLine(Marshal.SizeOf(typeof(IntPtr)));
		Console.WriteLine(Marshal.SizeOf(typeof(bool)));
		Console.WriteLine(Marshal.SizeOf(typeof(C)));
		Console.WriteLine(Marshal.SizeOf(typeof(S)));
		Console.WriteLine();
		Print();
	}
	
	static unsafe void Print()
	{
		Console.WriteLine(sizeof(IntPtr));
		Console.WriteLine(sizeof(bool));
		//Console.WriteLine(sizeof(C));
		Console.WriteLine(sizeof(S));
	}
}

sizeof kann nur die Größe von Wertetypen berechnen (daher Marshal.SizeOf für eine Klasse [mit explizitem StructLayout(LayoutKind.Sequential)-Attribut]).
Es sollte


8
4
1
1

8
1
1

herauskommen (bei 64 bit).
(der Interop-Wert für bool ist 4, aber das liegt an dem WinAPI-Datentyp BOOL, daher ist 1 Byte die .NET-Größe)

16.806 Beiträge seit 2008
vor einem Jahr

Bei Wertetypen wird direkt der Wert gespeichert,

.. auch nicht bei allen 🙂
Ein struct mit In-Parameter wird zur Referenz.

Sustainable Code - In Param
Erklärung dazu: https://twitter.com/marcgravell/status/1520297502771142657

4.931 Beiträge seit 2008
vor einem Jahr

Das bezieht sich aber nicht auf die Datenspeicherung [im Heap] (z.B. als Member in einer Klasse oder in einer List<T> oder Collection<T>), sondern nur für die Übergabe als Methodenparamater (d.h. auf dem Stack).

6.911 Beiträge seit 2009
vor einem Jahr

Hallo jogibear9988,

mach ein Speicherabbild und analysiere das. Dann siehst du genau welcher Objekt am größten ist, etc.
Das geht am einfachsten mit dem dotnet-dump Tool.

Danach kannst du überlegen wie Objekte geteilt werden, etc.
So pauschal ohne den Code zu kennen, kann ich keine konkrete Hilfe geben.

mfG Gü

Stellt fachliche Fragen bitte im Forum, damit von den Antworten alle profitieren. Daher beantworte ich solche Fragen nicht per PM.

"Alle sagten, das geht nicht! Dann kam einer, der wusste das nicht - und hat's gemacht!"