First, single-core serialize all "unique" data that only occurs once (faction relations, player state, known encyclopedia entries, quest status, etc), just like it happens now. Write that into the savegame.
Now the savegame content so far would look like (very much abstracted):
[unique data]
Then use multithreading to process the "repeating" data, aka ships, stations, ai orders, etc that theoretically could be written in any order (whether i write ship/station data for sector A or sector B first into the savegame shouldn't matter, its the savegame loader that'll have to stitch together the cross references between objects) :
First, make a list of references to all sectors in the universe.
Then, have each core take one sector reference each out of that list and serialize their sectors content (owner/ships/stations/projectiles/boxes/etc) to a temporary buffer in RAM. While traversing the object hierarchy of this sectors objects, if an object in this sector references a ship/station/etc outside the currently examined sector, stop traversing this graph path (each sector-serialization job only cares about its own sector), just save the reference of the not-in-this-sector-object (another core will take care of the object in the other sector) and continue traversing the graph in the current sector.
If the entire sector graph has been traversed and no other core is currently writing its buffer to the savegame, append current sectors byte buffer to the savegame. If another core is currently writing to the save file, this core has to wait until the other core is done. After the first sector write, the savegame content would look like (again, very simplified, the 0x... is simply the reference ID for that object)
[unique data]
[sectorA][sector data][ship in that sector:0x123456][ship data][ship in that sector:0x257941][ship data][station in that sector:0x321124][station data][etc data in that sector] (loot, wrecks, etc and so on)
After the second sector and its content is written, the savegame would look like this :
[unique data]
[sectorA][sector data][ship:0x123456][ship data][ship:0x257941][ship data][station:0x321124][station data][etc data in that sector] (loot, wrecks, projectiles, etc and so on)
[sectorB][sector data][ship:0x364792][ship data][ship:0x335412][ship data][station:0x145030][station data][etc data in that sector] (loot, wrecks, projectiles, etc and so on)
After a core appended the buffer into the savegame, clear the buffer and pick the next unprocessed sector in the list.
Repeat until no more sectors are in the list.
You'd end up with a list of all sectors, and after each sector entry there'd be a list of data about all objects in that sector. Theoretically the object graph traversal speed for examining and serializing the universe could go up with each additonal core available. Depending on how X4 loads a savegame/game object, loading a game could perhaps also be partially multithreaded, again with each thread loading one sector at a time, and after all sectors/objects are loaded, another pass over everything would fix up the references.
The main issue i'd guess is that X4, if i remember the savegame format correctly, currently uses a totally different format/object order/XML schema for storing game data than what my multitreaded idea would produce.

Ohwell...