PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : .NET AnyCPU Assembly erzeugen, dass x86/x64 Assemblies referenziert


PatkIllA
2011-08-18, 17:23:27
Ich habe ein Assembly das keinen CPU spezifischen Code enthält und je nach System automatisch in 32 bzw 64 Bit laufen soll.
Dort benutze ich allerdings ein Assembly, dass getrennte Versionen für x86 und x64 benutzt. (konkret SQLite)
Wie kriege ich es jetzt am geschicktesten hin, dass das jeweils passende SQLite Assembly angezogen wird?
Ich meine ich hätte mal irgendwas mit app.config gesehen.

Beim invoken von unmanged code dachte ich an folgendes Konstrukt:

static class NativeDllHelper
{
[DllImport(@"x86\lib.dll", EntryPoint = "FunctionName")]
private static extern int FunctionNameX86(string fileName, IntPtr pointer);

[DllImport(@"x64\lib.dll", EntryPoint = "FunctionName")]
private static extern int FunctionNameX64(string fileName, IntPtr pointer);

public static int FunctionName(string fileName, IntPtr pointer)
{
if (IntPtr.Size == 4)
return NativeDllHelper.FunctionNameX86(fileName, pointer);
else
return NativeDllHelper.FunctionNameX64(fileName, pointer);
}
}

Monger
2011-08-18, 17:26:44
Man kann das durch Deployment lösen - und ich glaube, das wird auch oft so gemacht:

Auf einem 32Bit System wird Assembly X an den Zielort kopiert, unter 64Bit eben Assembly Y. Da die Assemblies zur Laufzeit geladen werden, wird halt die geladen die gerade da ist.
Das hätte dann auch pauschal erstmal gar nichts mit deinem Compile Target zu tun.

PatkIllA
2011-08-18, 17:31:56
Das schöne ist, dass man jetzt einfach nur eine Auslieferung hat. Das sollte idealerweise eigentlich auch so bleiben.
Wir benutzen eh schon eine eigene Auflösung der Assemblies zu Dateinamen, da wir das Projekte in der Maximalauslieferung mittlerweile bei 160 Assemblies angekommen ist und da mit Unterordnern arbeiten. Kann man darauf aufsetzen?

][immy
2011-08-18, 17:33:59
eine möglichkeit ist, die DLLs in den GAC (windows\assemblies) zu legen. Dann würde .net ebenfalls automatisch entscheiden welche Version geladen wird.
ansonsten nur per "vorprozess" (z.B. batch script) der dann die bin datei entsprechend austauscht.

eine weitere möglichkeit wäre es, die DLL einfach manuell (z.B. per reflection) zu laden, und zwar bevor du den ersten befehl auf diese DLL schickst. Dann würde .net hingehen und die DLL nehmen, die sich bereits im Speicher befindet, solange die Signatur übereinstimmt.

Gast
2011-08-20, 22:46:12
Bis auf sehr wenige Ausnahmen, macht es eigentlich keinen Sinn .NET Programme als 64Bit laufen zu lassen. Umgekehrt gibt es sehr viele Dinge, die als 64Bit Probleme machen (ODBC Treiber, ActiveX Steuerelemente, diverse 32Bit dlls etc.) Ich würde einfach alle Komponenten auf x86 festlegen und schon sind alle Probleme gegessen.

PatkIllA
2011-08-20, 22:52:28
Wir liegen schon regelmäßig bei 1 GB Speicherverbrauch und die Datenbestände sind oft etliche Gigabyte groß.
Im Moment zieht die exe als x86 only das Programm runter. Bis auf zwei Komponenten (eine kann man umgehen, die andere wird nur noch als Kompatibilität zu Altdaten verwendet) gibt es mittlerweile auch alles als 64 Bit.

Ein bisschen akademischer Ehrgeiz ist aber auch dabei.

][immy
2011-08-20, 23:33:32
Bis auf sehr wenige Ausnahmen, macht es eigentlich keinen Sinn .NET Programme als 64Bit laufen zu lassen. Umgekehrt gibt es sehr viele Dinge, die als 64Bit Probleme machen (ODBC Treiber, ActiveX Steuerelemente, diverse 32Bit dlls etc.) Ich würde einfach alle Komponenten auf x86 festlegen und schon sind alle Probleme gegessen.
gerade bei server apps ist man mit .net ruck zuck auf nem GB. Leider ist bei 32-bit in .net schon bei 1,2 bis 1,4 GB schluss (frag mich jetzt nicht warum).
nachteil an .net/java ist halt, das man nicht mehr so sehr auf eine optimierung des speicherbedarfs achtet, sondern eher auf eine optimierung der geschwindigkeit. das spart jede menge entwicklungszeit und bei heutigen server-systemen sollte speicher nicht mehr der limitierende faktor sein.

es gibt aber auch folgeprobleme die durch das festlegen von x64 bzw. x86 passieren können. Z.B. würde man eine Assembly auf x86 festlegen und später will jemand anders deine Logik weiterverwenden, dann hätte derjenige schon das Problem, das er auf x86 gezwungen wird (inkl. aller abhängigkeiten) D.h. zumindest die Logik-DLLs sollte man auf jeden Fall nicht festlegen. Wenn es denn so sein sollte, das man 64/32 bit erzwingen will, am besten eine Startdatei die dann entsprechend fest kompiliert wurde. Spart später einige Probleme. Ich für meinen Teil fluche heute schon oft genug wenn ich sehe, das einige APIs nur in 32-bit angeboten werden.

Und wie gesagt, du solltest es per Reflection lösen können in dem du die korrekte DLL vor der Nutzung (wichtig ist, das kein Aufruf innerhalb der gleiche Methode vorkommt, da .net Methodenweise kompiliert) in die App--Domain lädst.

Ansonsten wäre da ja noch die möglichkeit die Wrapper-DLL in 32 und 64 Bit in den GAC zu legen und .net entscheidet es automatisch.

zudem könntest du die unmanged DLL jeweils in das System32 (x64) und Wow64 (x86) legen. allerdigns müsstest du dann im Setup entscheiden welche DLL du in die entsprechenden Ordner legst.

Die Methode mit dem GAC würde mir am ehesten zusagen. Dürfen Kunden halt nur keine Sicherheitsbedenken haben.

PatkIllA
2011-08-20, 23:36:23
Ein Vorteil ist dass man das einfach so hinlegen und ausführen kann .
Ins system Verzeichnis packen wir ganz sicher nichts und beim GAC ist unser Versionswirrwar noch zu hoch.

Coda
2011-08-21, 16:47:39
[immy;8895676']gerade bei server apps ist man mit .net ruck zuck auf nem GB. Leider ist bei 32-bit in .net schon bei 1,2 bis 1,4 GB schluss (frag mich jetzt nicht warum).
Interessant.

Eigentlich würde man denken, das durch Heap Compaction bei einem GC eher mehr verwendet werden kann als normal mit möglicher Fragmentierung.

Würde mich auch interessieren, was dafür der Grund ist.

PatkIllA
2011-08-21, 17:20:38
Ich bin auch noch nie über 1,4 GB gekommen, es sei denn man verwendet unsafe code.

Plus halt die OutOfMemoryException, die GDI bzw (System.Drawing) gelegentlich wirft obwohl eigentlich noch jede Menge Speicher frei ist.

Warum ist die Express Edition eigentlich auf 32 Bit beschränkt?

Demirug
2011-08-21, 17:41:37
Interessant.

Eigentlich würde man denken, das durch Heap Compaction bei einem GC eher mehr verwendet werden kann als normal mit möglicher Fragmentierung.

Würde mich auch interessieren, was dafür der Grund ist.

Der Server GC ist einfach extrem lazy solange noch genügend physikalischer Speicher vorhanden ist wird er in den höheren Generationen einfach sehr selten aktiv. Wenn man dann ein wenig ungeschickt programmiert hat steigt die Nutzung sehr schnell auf das was an RAM vorhanden ist an. Zudem kann man auch bei .Net immer noch „Speicherleaks“ erzeugen. Ich habe beobachtet das Entwickler wenn ein GC vorhanden ist dazu neigen diesem die Schuld zu geben und nicht nach diesen Problemen suchen.

Zudem gibt es auch noch einen sehr bösen Speicherbug in der Link to SQL Implementierung. Normalerweise merkt man davon nicht aber bei einem Server der 24/7 mit vielen DB Aktionen läuft kann nach ein paar Tagen der Speicher weg sein. Das primäre Problem dabei ist aber das vorher die GC Operationen schon unerträglich langsam werden.

PatkIllA
2011-08-21, 17:44:18
@Demirug
du fliegst aber auch schon bei 1,4 GB raus, wenn du einfach nur am Stück Arrays anlegst.

Exxtreme
2011-08-21, 18:06:38
Das mit den 1,4 GB liegt daran, dass der RAM wohl fragmentiert ist und das OS den angeforderten RAM am Stück nicht findet. Dann schmeisst es eine Exception.

PatkIllA
2011-08-21, 18:07:56
Das mit den 1,4 GB liegt daran, dass der RAM wohl fragmentiert ist und das OS den angeforderten RAM am Stück nicht findet. Dann schmeisst es eine Exception.
Ich hab es gerade noch mal getestet und das passiert auch wenn ich nur ein paar Kilobyte große Arrays baue und in eine Liste packe.

Man müsste mal schauen, was passiert, wenn man die exe mit LAA-Flag versieht.

Coda
2011-08-21, 19:24:02
@Demirug
du fliegst aber auch schon bei 1,4 GB raus, wenn du einfach nur am Stück Arrays anlegst.
Das ist auch bei native code nicht anders. Der virtuelle Speicherraum hat leider unbenutzbare "Löcher" irgendwo in der Mitte auf Windows.

Edit: Du meinst kein einziges großes, oder?

PatkIllA
2011-08-21, 19:28:24
Das ist auch bei native code nicht anders. Der virtuelle Speicherraum hat leider unbenutzbare "Löcher" irgendwo in der Mitte auf Windows.

Edit: Du meinst kein einziges großes, oder?
Ich meine viele kleine. Ich hatte jetzt 32 KiB genommen aus jeweils 8192 ints genommen.
Wenn ich das gleiche mit
IntPtr ptr = Marshal.AllocHGlobal(blockSize);
Marshal.Copy(source, 0, ptr, length);mache komme ich auf knapp 1,9 GB belegt bevor der Speicher ausgeht.

Coda
2011-08-21, 19:34:12
1,9GB ist doch fast genau die theoretische Grenze von 2GiB. Wo ist dann das Problem?

PatkIllA
2011-08-21, 19:41:11
1,9GB ist doch fast genau die theoretische Grenze von 2GiB. Wo ist dann das Problem?
Das ist ja unmanaged Code. Wenn man managed Arrays macht, ist bei den erwähnten 1,4 GB Schluss

Exxtreme
2011-08-21, 19:43:35
Ich hab es gerade noch mal getestet und das passiert auch wenn ich nur ein paar Kilobyte große Arrays baue und in eine Liste packe.

Man müsste mal schauen, was passiert, wenn man die exe mit LAA-Flag versieht.
Das LAA-Flag wird nichts bringen, höchstens auf 64-Bit-Systemen und da kann man gleich das 64-Bit-Assembly nutzen. Und vom boot.ini-Schalter empfehle ich dringend die Finger zu lassen.

Und ja, das passiert auch mit kleinen Arrays. Nur steigt die Wahrscheinlichkeit, dass es bei großen Arrays früher passiert. Wie schon geschrieben, der angeforderte RAM muss am Stück(!) und nicht irgendwie quer durch den Adressraum verteilt verfügbar sein. Je mehr RAM man am Stück anfordert desto größer die Wahrscheinlichkeit, dass es fehlschlägt. Es ist ein Unterschied ob man 1x 1 MB anfordert oder 100x 10 KB.

Und wenn ihr jetzt schon solche Probleme mit dem Adressraum habt dann würde ich sämtlich 32-Bit-Kisten entsorgen und komplett auf 64-Bit umsteigen.

PatkIllA
2011-08-21, 19:51:18
Und wenn ihr jetzt schon solche Probleme mit dem Adressraum habt dann würde ich sämtlich 32-Bit-Kisten entsorgen und komplett auf 64-Bit umsteigen.Normalerweise reicht das noch. Man kriegt es aber hin, wenn man doch mal etliche Module und einige Tabs aufhat. Plus das es ein paar Analyse-Funktion gibt, wo schon mal hundert tausende von Objekten (also noch ein vielfaches an .NET-Objekten).
Außerdem sind wir der Anbieter. Die meisten Kunden haben eh noch XP und es sind noch zwei 32 Bit only Komponenten drin.

Exxtreme
2011-08-21, 19:59:21
OK, dann wird es eine Herausforderung. :D

Ich würde auf jeden Fall so wenig wie möglich Speicher am Stück anfordern. Und wenn viele Objekte nicht mehr benötigt und zum Löschen freigegeben sind dann den Garbage Collector manuell aufrufen. Nur kann es passieren, dass die Anwendung kurz hängt wenn der GC läuft.

][immy
2011-08-21, 23:04:59
OK, dann wird es eine Herausforderung. :D

Ich würde auf jeden Fall so wenig wie möglich Speicher am Stück anfordern. Und wenn viele Objekte nicht mehr benötigt und zum Löschen freigegeben sind dann den Garbage Collector manuell aufrufen. Nur kann es passieren, dass die Anwendung kurz hängt wenn der GC läuft.

viele kleine Stückchen anfordern hat aber den nachteil das man den GC mehr arbeit aufhalst ;)
aber grundsätzlich hast du schon recht. man muss aber meist immer einen guten mittelweg finden.

das mit den 32-bit XP system finde ich jetzt nicht so schlimm. zumindest ich hab meistens eh damit zu tun, das alles aus dem Browser heraus funktionieren muss, wobei man hier auch schnell das speicherlimit erreichen kann.
Bei winforms oder WPF habe ich das Limit noch nicht erreicht. Hier hat man meist eh den vorteil, das man nur einen kleinen teil im speicher benötigt.

Was aber wichtig ist (darauf bin ich auch schon mal reingefallen) keine methoden, die z.B. mehr oder minder ewig loopen und logik ausführen. der GC räumt die erzeugten objekte erst nach dem verlassen einer methode ab. D.h. erzeugt man objekte in einem (mehr oder minder) endlosen loop, bleiben diese im speicher. nutzt man innerhalb des loops eine methode um die logik auszuführen, räumt der GC auch schön alles ab.

Immer wieder schön wenn man alt-code bestände übernimmt wo (aus welchen Gründen auch immer) alles in eine methode gepackt wurde und man nach einer langen laufzeit auf "Out of Memory"-Exceptions trifft, obwohl an sich alles abgeräumt wird. :)


warum kommt es eigentlich manchmal bei diesen OutOfMemory-Exceptions dazu, das .net die letzte Methode quasi in einer endlosschleife ausführt. Hatte ich schon ein paar mal. Der GC lief anscheinend, hat ein wenig was abgeräumt, dann hat er die Methode erneut versucht auszuführen, worauf wieder eine OOM exception kam und das ganze von vorne los ging. Abgefangen wurden die Fehler an sich nicht und ein eigener Loop wurde auch nicht implementiert.