UNO DEV HA OTTIMIZZATO IL TEMPO DI CARICAMENTO DI GTA ONLINE DEL 70%

UNO DEV HA OTTIMIZZATO IL TEMPO DI CARICAMENTO DI GTA ONLINE DEL 70%

1 Marzo 2021 Off Di Noctis Lucis Caelum

GTA Online. Famoso per i suoi tempi di caricamento lenti. Avendo ripreso il gioco per finire alcune delle rapine più recenti, il dev. T0ST è rimasto scioccato nello scoprire che si carica ancora lentamente come il giorno in cui è stato rilasciato 7 anni fa.

Questa scioccante verità lo ha spinto a scoprire il perchè:

Per prima cosa voleva verificare se qualcuno avesse già provato a risolvere questo problema. La maggior parte dei risultati che ha trovato indicavano aneddoti su come il gioco fosse così sofisticato da dover essere caricato così lentamente, storie su come l’architettura di rete p2p sia pessima, alcuni modi elaborati di caricamento nella modalità storia… Qualche lettura in più gli ha dato modo di scoprire che avrebbe potuto risparmiare almeno 10-30 secondi di caricamento!

Nel frattempo un sondaggio ha tirato le righe sui malcontenti dovuti ai tempi di caricamento di GTA online.

Cercando di capire chi è il fortunato ~ 20% che ottiene tempi di caricamento inferiori a 3 minuti, si è imbattuto in alcuni benchmark con PC da gioco di fascia alta … Sembra dipendere dall’hardware, ma qui qualcosa non torna … Armato di strumenti così potenti come il Task Manager, ha iniziato a indagare su quali risorse potrebbero essere le cause del collo di bottiglia.

Dopo aver impiegato un minuto per caricare le risorse comuni utilizzate per le modalità storia e online, GTA decide di massimizzare un singolo core sulla mia macchina per quattro minuti e non fare nient’altro.

  • Uso del disco? Nessuna!
  • Utilizzo della rete? Un po ‘, ma fondamentalmente scende a zero dopo pochi secondi (a parte il caricamento dei banner informativi in rotazione).
  • Utilizzo della GPU? Zero.
  • Utilizzo della memoria? Completamente piatto

A questo punto il dev. sente odore di codice, codice davvero pessimo. La cosa strana è che il caricamento di GTA online utilizza solo la CPU. Come minimo ci sarebbe da aspettarsi grandi quantità di letture del disco che caricano risorse o carichi di richieste di rete che cercano di negoziare una sessione nella rete p2p. Ma questo? Questo è probabilmente un bug.

I profiler sono un ottimo modo per trovare i colli di bottiglia della CPU. C’è solo un problema: la maggior parte di loro fa affidamento sulla strumentazione del codice sorgente per ottenere un’immagine perfetta di ciò che sta accadendo nei processi. Normalmente il tool Luke Stackwalker (non aggiornato da 10 anni) raggrupperebbe le stesse funzioni insieme, ma poiché il dev. non ha simboli di debug, ha dovuto guardare gli indirizzi vicini per indovinare e cosa ha trovato? Non un collo di bottiglia ma due!

Via con il disassemblatore. Il codice iniziale non sembra affatto giusto. La maggior parte dei giochi di alto profilo è dotata di protezione integrata contro il reverse engineering per tenere lontani pirati, imbroglioni e modder. Non che li abbia mai fermati. In questo caso, sembra esserci una sorta di offuscamento / crittografia in gioco che ha sostituito la maggior parte delle istruzioni con parole senza senso.

Le istruzioni devono essere de-offuscate prima di essere eseguite in un modo o nell’altro. Lo smontaggio della copia di memoria ora meno offuscata rivela che uno degli indirizzi ha un’etichetta estratta! È strlen? Scendendo nello stack di chiamate, quello successivo è etichettato vscan_fn e dopo che le etichette finiscono, c’è sscanf.

Sta analizzando qualcosa: ma cosa? Districare lo smontaggio avrebbe richiesto un’eternità, quindi il dev. ha deciso di scaricare alcuni campioni dal processo in esecuzione usando x64dbg. Dopo alcuni passaggi di debug si scopre che è … JSON! Stanno analizzando JSON. Un enorme valore di 10 megabyte di JSON con circa 63.000 elementi.

...,
{
    "key": "WP_WCT_TINT_21_t2_v9_n2",
    "price": 45000,
    "statName": "CHAR_KIT_FM_PURCHASE20",
    "storageType": "BITFIELD",
    "bitShift": 7,
    "bitSize": 1,
    "category": ["CATEGORY_WEAPON_MOD"]
},
...

Che cos’è? Secondo alcune referenze, sembra trattarsi di dati per un “catalogo del negozio”. Si presume che contenga un elenco di tutti i possibili oggetti e aggiornamenti che puoi acquistare in GTA Online, con denaro di gioco, non con le microtransazioni. Ma 10 mega? Non è niente! E l’utilizzo di sscanf potrebbe non essere ottimale, ma sicuramente non è così male?

Si scopre che il secondo colpevole viene chiamato proprio accanto al primo. Entrambi sono persino chiamati nella stessa istruzione if come si vede in questa brutta decompilazione:

Il secondo problema? Subito dopo aver analizzato un elemento, viene memorizzato in un array (o in un elenco C ++ inline? non si capisce). Ogni voce ha un aspetto simile a questo:

struct {
    uint64_t *hash;
    item_t   *item;
} entry;

Prima che venga archiviato controlla l’intero array, uno per uno, confrontando l’hash dell’elemento per vedere se è nell’elenco o meno. La maggior parte di loro inutili. Ci sono hash unici, perché non è stata utilizzata una mappa hash? L’elemento hash-array-list è vuoto prima di caricare il JSON. E tutti gli elementi nel JSON sono unici!

Il piano del dev: scrivere una dll inserirla in GTA e agganciarla ad alcune funzioni. Fine.

Il problema JSON è complicato, non può essere sostituito il loro parser. Sostituire sscanf con uno che non dipende da strlen sarebbe più realistico. Ma c’è un modo ancora più semplice.

  • hook strlen
  • aspetta una lunga stringa
  • “Cache” l’inizio e la lunghezza di esso
  • se viene chiamato di nuovo all’interno dell’intervallo della stringa, restituisci il valore memorizzato nella cache

E per quanto riguarda il problema dell’array hash, è più semplice: salta completamente i controlli duplicati e inserisci gli elementi direttamente poiché sappiamo che i valori sono univoci. il risultato:

Tempo di caricamento della modalità online originale: ~ 6m
Tempo con la sola patch di controllo della duplicazione: 4m 30s
Tempo con la sola patch del parser JSON: 2 m 50 s
Tempo con entrambi i problemi risolti: 1 m 50 s
miglioramento del tempo di caricamento del 69,4% (bello!)

Qui, il poc di prova scritto dal dev.

Molto probabilmente, questo non risolverà i tempi di caricamento di tutti: potrebbero esserci altri colli di bottiglia su sistemi diversi, ma è un buco così grande che non si comprende il motivo per cui R * l’abbia mancato in tutti questi anni.

  • C’è un collo di bottiglia della CPU a thread singolo durante l’avvio di GTA Online
  • Si scopre che GTA fatica ad analizzare un file JSON da 10 MB
  • Il parser JSON stesso è mal costruito / ingenuo
  • Dopo l’analisi c’è una lenta routine di de-duplicazione degli elementi

Se questo in qualche modo raggiunge Rockstar: i problemi non dovrebbero richiedere più di un giorno per essere risolti da un singolo sviluppatore. La soluzione è anticipata dal dev.: “È possibile passare a una hashmap per la de-duplicazione o saltarla completamente all’avvio come soluzione più rapida. Per il parser JSON, basta sostituire la libreria con una più performante. Non credo che ci sia una via d’uscita più facile. A te <3

Fonte: T0ST da neel.lv