Il mio mal di testa a 32 bit ora è un'emicrania a 64 bit?!? (o problemi di runtime CLR .NET a 64 bit)

StackOverflow https://stackoverflow.com/questions/635019

  •  10-07-2019
  •  | 
  •  

Domanda

Quali conseguenze insolite e inaspettate si sono verificate in termini di prestazioni, memoria, ecc. quando si passa dall'esecuzione delle applicazioni .NET a JIT a 64 bit rispetto a JIT a 32 bit? Sono interessato ai buoni, ma più interessato ai problemi sorprendentemente cattivi che le persone hanno incontrato.

Sto scrivendo una nuova applicazione .NET che verrà distribuita sia a 32 bit che a 64 bit. Ci sono state molte domande relative ai problemi con il porting dell'applicazione - Non mi preoccupo di " gotchas " dal punto di vista della programmazione / porting . (es .: gestione corretta dell'interoperabilità nativa / COM, tipi di riferimento incorporati in strutture che modificano le dimensioni della struttura, ecc.)

Tuttavia, questa domanda e la sua risposta mi hanno fatto pensare - Quali altri problemi sto trascurando?

Ci sono state molte domande e post sul blog che aggirano questo problema o ne toccano un aspetto, ma non ho visto nulla che abbia compilato un elenco decente di problemi.

In particolare - La mia applicazione è molto legata alla CPU e ha enormi schemi di utilizzo della memoria (quindi la necessità di 64 bit in primo luogo), oltre ad essere di natura grafica. Mi preoccupo di quali altri problemi nascosti potrebbero esistere nel CLR o JIT in esecuzione su Windows a 64 bit (utilizzando .NET 3.5sp1).

Ecco alcuni problemi di cui sono attualmente a conoscenza:

Mi piacerebbe sapere quali altri problemi specifici hanno scoperto nella JIT su Windows a 64 bit e anche se ci sono soluzioni alternative per le prestazioni.

Grazie a tutti!

---- EDIT -----

Giusto per chiarire -

Sono consapevole del fatto che cercare di ottimizzare in anticipo è spesso negativo. Sono consapevole che la seconda ipotesi del sistema è spesso negativa. So anche che la portabilità a 64 bit ha i suoi problemi: eseguiamo e testiamo su sistemi a 64 bit ogni giorno per aiutarci. ecc.

La mia applicazione, tuttavia, non è la tipica applicazione aziendale. È un'applicazione software scientifica. Abbiamo molti processi che utilizzano la CPU al 100% su tutti i core (è altamente threaded) per ore alla volta.

Trascorro MOLTO tempo a profilare l'applicazione, e questo fa una grande differenza. Tuttavia, la maggior parte dei profilatori disabilita molte funzionalità di JIT, quindi i piccoli dettagli in cose come l'allocazione della memoria, l'integrazione in JIT, ecc., Possono essere molto difficili da individuare quando si esegue un profiler. Da qui il mio bisogno della domanda.

È stato utile?

Soluzione

Ricordo di aver sentito un problema da un canale IRC che frequento. Ottimizza la copia temporanea in questo caso:

EventHandler temp = SomeEvent;
if(temp != null)
{
    temp(this, EventArgs.Empty);
}

Ripristino delle condizioni di gara e causa potenziali eccezioni di riferimento null.

Altri suggerimenti

Un problema di prestazioni particolarmente problematico in .NET riguarda la scarsa JIT:

https :? //connect.microsoft.com/VisualStudio/feedback/details/93858/struct-methods-should-be-inlined wa = wsignin1.0

Fondamentalmente, allineamento e strutture non funzionano bene insieme su x64 (anche se quella pagina suggerisce ora allineamento funziona ma le successive copie ridondanti non vengono eliminate, sembra sospetto data la piccola differenza perfetta) .

In ogni caso, dopo aver lottato con .NET abbastanza a lungo per questo, la mia soluzione è usare C ++ per qualsiasi cosa numericamente intensiva. Anche in "buono" casi per .NET, dove non hai a che fare con le strutture e usi array in cui il controllo dei limiti è ottimizzato, C ++ batte .NET giù le mani .

Se stai facendo qualcosa di più complicato dei prodotti a punti, l'immagine peggiora molto rapidamente; il codice .NET è più lungo + meno leggibile (perché è necessario incorporare manualmente le cose e / o non è possibile utilizzare generici) e molto più lento.

Sono passato all'utilizzo di Eigen in C ++: è assolutamente fantastico , con conseguente codice leggibile e alte prestazioni; un sottile wrapper C ++ / CLI fornisce quindi la colla tra il motore di calcolo e il mondo .NET.

Eigen funziona mediante meta-programmazione di template; compila espressioni vettoriali in istruzioni intrinseche SSE e svolge molte delle operazioni più sgradevoli legate alla cache che si svolgono e si riorganizzano per te; e sebbene focalizzato sull'algebra lineare, funzionerà anche con espressioni di array interi e non-matrice.

Quindi, ad esempio, se P è una matrice, questo tipo di cose funziona semplicemente:

1.0 /  (P.transpose() * P).diagonal().sum();

... che non alloca una variante temporaneamente trasposta di P e non calcola l'intero prodotto matrice ma solo i campi di cui ha bisogno.

Quindi, se puoi eseguire Full Trust - usa C ++ via C ++ / CLI, funziona molto meglio.

La maggior parte delle volte Visual Studio e il compilatore fanno un ottimo lavoro nel nasconderti i problemi. Tuttavia, sono a conoscenza di un grave problema che può sorgere se imposti l'app per rilevare automaticamente la piattaforma (x86 vs x64) e anche hanno dipendenze da dll di terze parti a 32 bit. In questo caso, su piattaforme a 64 bit proverà a chiamare le DLL usando convenzioni e strutture a 64 bit, e semplicemente non funzionerà.

Hai menzionato i problemi di porting, di cui devi occuparti. (Ovviamente) non conosco la tua applicazione, ma cercare di indovinare la JIT è spesso una completa perdita di tempo. Le persone che scrivono il JIT hanno una comprensione intima dell'architettura del chip x86 / x64 e, con ogni probabilità, sanno cosa funziona meglio e cosa funziona peggio di chiunque altro al mondo.

Sì, è possibile che tu abbia un caso angolare diverso e unico, ma se stai per "scrivere una nuova applicazione" quindi non mi preoccuperei del compilatore JIT. C'è probabilmente un loop stupido che può essere evitato da qualche parte che ti farà guadagnare 100 volte il miglioramento delle prestazioni che otterrai provando a indovinare il JIT. Mi ricorda i problemi che abbiamo riscontrato durante la scrittura del nostro ORM, guardavamo il codice e pensavamo di poter estrarre un paio di istruzioni dal computer ... ovviamente, il codice è andato via e si è connesso a un server di database su una rete , quindi stavamo eliminando i microsecondi da un processo limitato da millisecondi da qualche altra parte.

Regola universale delle prestazioni ... Se non hai misurato le tue prestazioni non sai dove sono i tuoi colli di bottiglia, devi solo pensare che sai .. e probabilmente ti sbagli.

Informazioni sulla risposta di Quibblesome:

Ho provato a eseguire il seguente codice nel mio Windows 7 x64 in modalità Release senza debugger e NullReferenceException non è mai stato generato .

using System;
using System.Threading;

namespace EventsMultithreadingTest
{
    public class Program
    {
        private static Action<object> _delegate = new Action<object>(Program_Event);
        public static event Action<object> Event;

        public static void Main(string[] args)
        {
            Thread thread = new Thread(delegate()
                {
                    while (true)
                    {
                        Action<object> ev = Event;

                        if (ev != null)
                        {
                            ev.Invoke(null);
                        }
                    }
                });
            thread.Start();

            while (true)
            {
                Event += _delegate;
                Event -= _delegate;
            }
        }

        static void Program_Event(object obj)
        {
            object.Equals(null, null);
        }
    }
}

Credo che il 64 JIT non sia completamente sviluppato / portato per sfruttare le CPU dell'architettura a 64 bit, quindi ha dei problemi, potresti avere un comportamento 'emulato' dei tuoi assemblaggi che potrebbe causare problemi e comportamenti imprevisti. Vorrei esaminare i casi in cui questo può essere evitato e / o forse vedere se esiste un buon compilatore veloce 64 c ++ per scrivere calcoli e algoritmi critici. Ma anche se hai difficoltà a trovare informazioni o non hai tempo di leggere il codice disassemblato, sono abbastanza sicuro che l'eliminazione di calcoli pesanti al di fuori del codice gestito ridurrebbe qualsiasi problema tu possa avere & amp; aumentare le prestazioni [abbastanza sicuro che lo stiate già facendo, ma solo per citare :)]

Un profiler non dovrebbe influenzare in modo significativo i risultati dei tempi. Se le spese generali del profiler sono davvero " significative " quindi probabilmente non puoi spremere molto più velocità dal tuo codice e dovresti pensare a esaminare i colli di bottiglia dell'hardware (disco, RAM o CPU?) e l'aggiornamento. (Sembra che tu sia vincolato alla CPU, quindi è qui da dove cominciare)

In generale, .net e JIT ti liberano dalla maggior parte dei problemi di porting a 64 bit. Come sapete, ci sono effetti relativi alla dimensione del registro (modifiche all'utilizzo della memoria, marshalling al codice nativo, che richiede che tutte le parti del programma siano build a 64 bit native) e alcune differenze di prestazioni (mappa di memoria più grande, più registri, bus più ampi ecc.), quindi non posso dirti nulla di più di quello che sai già su quel fronte. Gli altri problemi che ho riscontrato sono OS anziché C #: ora ci sono diversi hive di registro per applicazioni a 64 bit e WOW64, quindi alcuni accessi al registro devono essere scritti con cura.

In genere è una cattiva idea preoccuparsi di cosa farà la JIT con il tuo codice e provare a regolarla per funzionare meglio, perché è probabile che la JIT cambi con .net 4 o 5 o 6 e le tue "ottimizzazioni"; può trasformarsi in inefficienze, o peggio, in bug. Inoltre, tieni presente che JIT compila il codice appositamente per la CPU su cui è in esecuzione, quindi potenzialmente un miglioramento sul tuo PC di sviluppo potrebbe non essere un miglioramento su un altro PC. Quello che ti toglie dall'uso della JIT di oggi sulla CPU di oggi potrebbe morderti tra un anno quando aggiorni qualcosa.

In particolare, citi le proprietà "non sono evidenziate in x64". Una volta eseguito l'intero codebase trasformando tutte le proprietà in campi, potrebbe esserci un nuovo JIT per 64 bit che include proprietà incorporate. In effetti, potrebbe avere un rendimento migliore rispetto alla tua "soluzione alternativa". codice. Lascia che Microsoft lo ottimizzi per te.

Fai giustamente notare che il tuo profilo di memoria può cambiare. Quindi potresti aver bisogno di più RAM, dischi più veloci per la memoria virtuale e cache della CPU più grandi. Tutti i problemi hardware. Potresti essere in grado di ridurre l'effetto utilizzando (ad esempio) Int32 anziché int ma ciò potrebbe non fare molta differenza e potrebbe potenzialmente danneggiare le prestazioni (poiché la tua CPU può gestire i valori nativi a 64 bit in modo più efficiente rispetto ai valori a 32 bit di mezza dimensione ).

Dici che i tempi di avvio possono essere più lunghi, ma ciò sembra piuttosto irrilevante in un'applicazione che dici che funziona per ore con CPU al 100%.

Quindi di cosa sei veramente preoccupato? Forse cronometra il tuo codice su un PC a 32 bit e poi esegui la stessa operazione su un PC a 64 bit. C'è mezz'ora di differenza su una corsa di 4 ore? O la differenza è solo di 3 secondi? O il PC a 64 bit è effettivamente più veloce? Forse stai cercando soluzioni a problemi che non esistono.

Quindi torniamo al solito, più generico, consiglio. Profilo e tempo per identificare i colli di bottiglia. Guarda gli algoritmi e i processi matematici che stai applicando e prova a migliorarli / sostituirli con altri più efficienti. Verifica che il tuo approccio al multithreading ti aiuti piuttosto che danneggiare le tue prestazioni (vale a dire che si evitano attese e blocchi). Prova a ridurre l'allocazione / deallocazione della memoria - ad es. riutilizzare gli oggetti anziché sostituirli con altri nuovi. Cerca di ridurre l'uso di chiamate di funzione frequenti e funzioni virtuali. Passa a C ++ e elimina le spese generali intrinseche di Garbage Collection, controllo dei limiti, ecc. Che .net impone. Hmmm. Niente di tutto ciò ha a che fare con 64 bit, vero?

Non ho molta familiarità con i problemi a 64 bit, ma ho un commento:

  

Dovremmo dimenticare il piccolo   efficienze, dire circa il 97% del   tempo: l'ottimizzazione prematura è il   radice di tutti i mali.   - Donald Knuth

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top