ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
About
  1. Blog

How I Cut GTA Online Loading Times by 70%

A reverse-engineering deep dive into why GTA Online took six minutes to load — and how two lines of quadratic code and a misunderstood libc function were to blame.

Aditya Challa·22 September 2026·7 min read

Summary
On this page
  1. Intro — what this post is (and is not)
  2. The starting point (author numbers)
  3. How the profiling was done
  4. Problem one: sscanf that keeps calling strlen
  5. Problem two: not\_a\_hashmap (quadratic insert)
  6. The proof-of-concept fix (author's approach)
  7. Author before / after (vendor claims)
  8. What did not help (and the real lesson)
  9. Aftermath (Rockstar)
  10. FAQ
  11. Is this a ShopperCove hands-on lab?
  12. Should I inject a community DLL to "fix" loads today?
  13. Why does this belong next to latency / monitoring posts?
  14. Where is the original writeup?
  15. Verdict
  16. Related ShopperCove posts

Intro — what this post is (and is not)

GTA Online was infamous for multi-minute loads long after launch. In February 2021, an independent reverse-engineering writeup (How I cut GTA Online loading times by 70%) showed why: not disk, not the peer-to-peer session, but a single-threaded CPU stall while parsing a huge JSON shop catalog — with a quadratic duplicate check on top.

This ShopperCove post is a clean, hands-on-toned walkthrough of that public writeup. It is not a ShopperCove lab on our box. Every timing below is the original author's measurement on their hardware unless labeled otherwise. Rockstar later shipped a fix; we cite that update honestly.

Related links:

  • Why your average latency graph is lying (p50 / p95 / p99)
  • How to read server monitoring graphs
  • Go GC + Swap on a Tiny VPS

Attribution bar: Source author hardware — AMD FX-8350, Kingston SA400S37120G SSD, 16 GB DDR3-1337, GTX 1070. Measurement window: Rockstar logo → in-game, startup menu disabled, Social Club login excluded. Affiliates: 0.


The starting point (author numbers)

On that machine, story mode loaded in about 1 minute 10 seconds. Online mode took roughly 6 minutes flat — about 5× longer on the same hardware. A Reddit poll cited in the writeup suggested more than 80% of respondents were annoyed by load times; high-end PCs in community benchmarks still sat around ~2 minutes online — better, but not close to story-mode parity.

Community workarounds (skip logo mods, story-then-online tricks) were described as saving only 10–30 seconds combined, if anything measurable. The author could not measure a difference from the story-to-online technique.


How the profiling was done

Task Manager was enough for the first pass. After about a minute loading resources shared with story mode, online load pegged a single CPU core for ~four minutes while doing essentially nothing else:

  • Disk usage near zero
  • Network dropping to near zero after the first few seconds (aside from rotating info banners)
  • GPU at zero
  • Memory flat

That ruled out I/O, network negotiation, and GPU work. The FX-8350's weaker single-thread performance could explain some of the gap versus faster CPUs — but a core stuck at 100% for minutes pointed at bad code, not "the game is sophisticated."

For closed-source software, the author used stack sampling (Luke Stackwalker) rather than an instrumenting profiler. Without debug symbols, matching addresses to the same function was done by eye. Two hot spots showed up, not one.

De-obfuscating a memory dump (Process Dump; the executable was obfuscated) and stepping with x64dbg revealed what was being parsed: JSON — about 10 MB with roughly 63,000 item entries for a "net shop catalog" of purchasable items and upgrades.

Example catalog entry shape (from the writeup):

{
  "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"]
}

Problem one: sscanf that keeps calling strlen

One hot path labeled as strlen, with vscan_fn and what looked like sscanf further down the stack. Many sscanf implementations call strlen internally to build an internal FILE-like object. Parsing values out of a large JSON blob with sscanf therefore re-scans the length of the remaining string on every call — turning "linear-looking" parsing into something far worse on a 10 MB buffer.


Problem two: not_a_hashmap (quadratic insert)

Right next to the parser, in the same if, each parsed item was inserted into an array of {hash, item} pairs — but only after a full linear scan comparing hashes to see if the item already existed.

With ~63,000 entries that is (n² + n) / 2 comparisons — about 1,984,531,500 by the author's math. The array started empty and every JSON key was unique, so the duplicate check was pointless. A direct-insert path already existed and could have been used instead.


The proof-of-concept fix (author's approach)

To prove the diagnosis, the author injected a DLL and hooked the hot paths (minhook). Full PoC source is linked from the original writeup on GitHub.

strlen path: cache start/end of a long string the first time its length is computed; return the cached length when the pointer falls inside that range; disable the hook once the remaining range shrinks below a threshold.

size_t strlen_cacher(char* str)
{
  static char* start;
  static char* end;
  size_t len;
  const size_t cap = 20000;

  if (start && str >= start && str <= end) {
    len = end - str;
    if (len < cap / 2)
      MH_DisableHook((LPVOID)strlen_addr);
    return len;
  }

  len = builtin_strlen(str);
  if (len > cap) {
    start = str;
    end = str + len;
  }
  return len;
}

Catalog insert path: skip the duplicate check and insert directly (keys known unique):

char __fastcall netcat_insert_dedupe_hooked(uint64_t catalog, uint64_t* key, uint64_t* item)
{
  uint64_t not_a_hashmap = catalog + 88;

  if (!(*(uint8_t(__fastcall**)(uint64_t*))(*item + 48))(item))
    return 0;

  netcat_insert_direct(not_a_hashmap, key, &item);

  if (*key == 0x7FFFD6BE) {
    MH_DisableHook((LPVOID)netcat_insert_dedupe_addr);
    unload();
  }

  return 1;
}

ShopperCove did not re-run this PoC. Treat it as the author's evidence package, not a recommendation to inject third-party DLLs into a live multiplayer client.


Author before / after (vendor claims)

From the original results table (same hardware, same measurement method):

ConditionOnline load (author)
Original~6m flat
Duplication-check patch only4m 30s
JSON / strlen patch only2m 50s
Both patches1m 50s

Author math: (6×60 − 110) / (6×60) ≈ **69.4%** reduction — the "70%" in the title.

Not ShopperCove lab numbers. Other machines may have different bottlenecks after the catalog path is fixed.


What did not help (and the real lesson)

Before the real bottleneck was found, the community had already tried:

  • Skipping the Rockstar logo via mods
  • Loading story mode first, then transitioning to solo online
  • Blaming "sophisticated" content or peer-to-peer architecture alone

Task Manager alone was enough to rule disk and network out during the multi-minute stall. The deeper lesson for operators and game engineers is the same one we keep hitting in server posts: profile before you assume, and beware accidentally quadratic code that looks fine on small test data and falls over on production-sized inputs.

Related links:

  • Why your average latency graph is lying (p50 / p95 / p99)
  • How to read server monitoring graphs

Aftermath (Rockstar)

Per the original author's updates (March 2021):

  • Rockstar confirmed a fix was coming and awarded a $10k H1 in-game bounty (unusual for a non-security performance bug).
  • An official update followed; the author reported the online load path as fully fixed on the same hardware measurement.

ShopperCove has not independently re-benchmarked current GTA Online builds on that 2013-era CPU setup.


FAQ

Is this a ShopperCove hands-on lab?

No. It is a cleaned rewrite of a public reverse-engineering post so the live CMS page stops showing raw markdown dumps and placeholders. Timings belong to the original author.

Should I inject a community DLL to "fix" loads today?

Do not treat this post as a how-to for online multiplayer clients. Rockstar shipped an official fix. Third-party injection risks bans and worse. The engineering takeaway is the profiling story.

Why does this belong next to latency / monitoring posts?

Same failure mode: averages and vibes hide quadratic or single-thread stalls. Measure the hot path.

Where is the original writeup?

  • nee.lv — How I cut GTA Online loading times by 70%
  • HN discussion linked from that page (item id 26296339)

Verdict

The writeup's diagnosis is clear and well evidenced by the author: online load spent minutes on a single core parsing ~10 MB of catalog JSON via an sscanf/strlen pattern, then inserted ~63k unique items with a full-array duplicate scan. Author-measured patches dropped ~6m → ~1m 50s (~69%). Rockstar later fixed it. ShopperCove's job here is honest attribution and a CMS-clean body — not invented lab numbers.

Affiliates: 0.


Related ShopperCove posts

  • Why your average latency graph is lying (p50 / p95 / p99)
  • How to read server monitoring graphs
  • Go GC + Swap on a Tiny VPS
  • JVM Heap vs RSS vs OOM on 512 MB
  • About ShopperCove
gta onlineloading timesc++reverse engineeringperformance optimizationjson parsingprofilingsscanf

Lab evidence

What I found running this

CMS FIX package (30 Sep 2026 IST). EDIT existing live post; keep slug cut-gta-online-loading-times-70. Body is a ShopperCove-style rewrite of the public nee.lv writeup (2021). All load-time numbers are the ORIGINAL AUTHOR's measurements on their AMD FX-8350 / Kingston SA400 / 16GB DDR3 / GTX 1070 — NOT a ShopperCove lab. No invented ShopperCove timings. Affiliates: 0. Remove broken Sources README dump and [AUTHOR PROFILING OUTPUT] / [AUTHOR BEFORE/AFTER] placeholders.

Sources

  1. 01
    How I cut GTA Online loading times by 70%

    GTA Online. [Infamous](https://www.reddit.com/r/gtaonline/comments/9vgo0g/how_the_fuck_are_20_minute_load_times_acceptable/) for its slow loading times. Having picked up the game again to finish some of the newer heists I was _shocked_ (/s) to discover that it still loads just as slow as the day it was released 7 years ago. It was time. Time to get to the bottom of this. ## [](#Recon "Recon")Recon First I wanted to check if someone had already solved this problem. Most of the results I found

  2. 02
    How I cut GTA Online loading times by 70%

    ![](https://news.ycombinator.com/s.gif) [](https://news.ycombinator.com/vote?id=26296735&how=up&goto=item%3Fid%3D26296339) Holy cow, I'm a very casual gamer, I was excited about the game but when it came out I decided I don't want to wait that long and I'll wait until they sort it out. 2 years later it still sucked. So I abandoned it. But.. this... ?! This is unbelievable. I'm certain that many people left this game because of the waiting time. Then there are man-years wasted (in a way dif

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

Related links

  • Plate 42

    Chrome DevTools AI Assistance (Gemini): Enable + Prompt Guide 2026

    1 Oct 2026

  • Plate 46

    Remix 3 Ships Oct 2, 2026: What Frontend Teams Need to Know

    1 Oct 2026

  • Plate 87

    Pennsylvania Measles Outbreak FAQ (October 2026)

    Factual FAQ on Pennsylvania’s 2026 measles outbreak: case counts, deaths, MMR basics, and where to find official CDC and state health updates.

    1 Oct 2026

On this page

  1. Intro — what this post is (and is not)
  2. The starting point (author numbers)
  3. How the profiling was done
  4. Problem one: sscanf that keeps calling strlen
  5. Problem two: not\_a\_hashmap (quadratic insert)
  6. The proof-of-concept fix (author's approach)
  7. Author before / after (vendor claims)
  8. What did not help (and the real lesson)
  9. Aftermath (Rockstar)
  10. FAQ
  11. Is this a ShopperCove hands-on lab?
  12. Should I inject a community DLL to "fix" loads today?
  13. Why does this belong next to latency / monitoring posts?
  14. Where is the original writeup?
  15. Verdict
  16. Related ShopperCove posts
All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove