Suggestion to improve data file loading from disk?

After much thinking I’m heavily leaning on the creation of everything at the start of the game instead as the player explored further and further. It doesn’t change anything in the way the game plays out, but it does remove some potentially threading issues as everything is created at the start of the game instead of during.

But, because of that, when the world creation is done, which takes about 20 secs of so, the game saves everything to disk, and that… that takes a lot more time than I’m willing to let it. (I didn’t time it, but it was quite a while when I did it)

Loading the game isn’t that much of an issue. It’s slower than I want it really, but it’s not that problematic. At least not now.

The main problem is saving at creation with a “small world” composed of about 500 sectors each containing about 500 systems.

The way I’m doing it right now is by having a directory for each sector and saving its data there. The base dir having the game state and other data.

As for when I load sector data, the latest example I have is from redoing the cache from zero:

Loading 27 sectors, for a total of about 11 MB, takes more or less 11 seconds.

Result output:

17:37:48.728 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #320] from disk) took 753 millisecs.
17:37:49.031 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #334] from disk) took 303 millisecs.
17:37:49.816 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #92] from disk) took 785 millisecs.
17:37:49.975 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #145] from disk) took 159 millisecs.
17:37:50.452 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #332] from disk) took 477 millisecs.
17:37:51.253 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #333] from disk) took 801 millisecs.
17:37:51.986 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #330] from disk) took 733 millisecs.
17:37:52.211 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #391] from disk) took 225 millisecs.
17:37:52.434 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #148] from disk) took 223 millisecs.
17:37:52.644 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #390] from disk) took 210 millisecs.
17:37:52.798 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #331] from disk) took 154 millisecs.
17:37:52.877 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #389] from disk) took 79 millisecs.
17:37:53.345 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #329] from disk) took 468 millisecs.
17:37:53.522 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #516] from disk) took 177 millisecs.
17:37:53.645 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #236] from disk) took 123 millisecs.
17:37:53.718 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #513] from disk) took 73 millisecs.
17:37:53.963 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #514] from disk) took 245 millisecs.
17:37:54.352 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #515] from disk) took 389 millisecs.
17:37:54.481 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #381] from disk) took 129 millisecs.
17:37:54.708 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #382] from disk) took 227 millisecs.
17:37:55.493 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #317] from disk) took 785 millisecs.
17:37:55.672 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #497] from disk) took 179 millisecs.
17:37:56.128 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #498] from disk) took 456 millisecs.
17:37:56.456 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #319] from disk) took 328 millisecs.
17:37:56.527 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #424] from disk) took 71 millisecs.
17:37:56.767 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #189] from disk) took 240 millisecs.
17:37:56.974 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #423] from disk) took 207 millisecs.

No real voodoo code in there
The loading loop:
[java] long realStart = System.currentTimeMillis();
long start = realStart;
long part1;
long part2;
for (Integer i : idsToAdd) {
cachedLeafs.add(TreeLeaf.loadLeaf(i));
part1 = System.currentTimeMillis();
LOGGER.debug(“Part 1 of cacheLeafs (loading [leaf #” + i + “] from disk) took " + (part1 - start) + " millisecs.”);
start = part1;
} [/java]

The loadLeaf method (load form disk)
[java]
static TreeLeaf loadLeaf(int leafID) {
boolean success = false;
FileInputStream fis;
ObjectInputStream in;
TreeLeaf outLeaf = null;

    String leafFilename = Utils.getSaveGamePath() + leafID + "\\leafData";

    // FIXME: Since saved leafs won't be committed until the user has
    // manually saved the game, we use ".temp" until then.
    // Look for these before loading default file.
    if (new File(leafFilename + ".temp").exists()) {
        leafFilename += ".temp";
    }

    try {
        fis = new FileInputStream(leafFilename);
        in = new ObjectInputStream(fis);
        outLeaf = (TreeLeaf) in.readObject();
        in.close();
        success = true;
    } catch (IOException | ClassNotFoundException ex) {
        Logger.getLogger(OctreeList.class.getName()).log(Level.SEVERE, null, ex);
        success = false;
    } finally {
        if (success) {
            return outLeaf;
        } else {
            throw new RuntimeException("Could not LOAD LEAF " + leafFilename);
        }
    }
}

[/java]


What I’m thinking of doing might be to use a single file, something similar to a compressed file (a la zip) into which I could dump the whole thing in memory and save when queried by user or whatever.

I’m sure some of you have suggestions or even potential fixes for better response time for deserialization of saved files.

As usual, thanks for all replies. :smiley:

Seems to me like it has nothing to do with threading or file system overhead, since loading the individual leaves takes a long time, that seems to be like slow de-serialization.

Have you tried wrapping the FileInputStream in a BufferedInputStream? You could try using jME3’s serialization or export the TreeLeaf directly without serialization at all, assuming its a simple enough data structure.

3 Likes

Momoko is right, wrapping your FileInputStream in a BufferedInputStream()… maybe with a 16k buffer or so (experiment) can improve the speed by an order of magnitude.

fis = new FileInputStream(leafFilename);
in = new ObjectInputStream(new BufferedInputStream(fis, 16384));

…or whatever. Give it a try.

3 Likes

Thanks guys, I’ll try this tomorrow and hopefully that’ll fix the annoyance I feel when loading/saving.

Seems like you have a large dataset that changes overtime. This is a use case for a database, not flat files.

Have you looked at SQLite or LevelDB? Both would allow you to have a single local file that stores everything and both have standalone libraries for Java.

SQLite is a traditional relational database, good if you need to do complex queries and lookups on the data.

LevelDB would be useful if you just want to get the data in and out as fast as possible.

Here is a comparison of speeds and use cases:

http://leveldb.googlecode.com/svn/trunk/doc/benchmark.html

They also list another one called TreeDB which might fit you needs even better.

With a good database, you should be able to stream the data off the disk as you need it, instead of caching everything upfront.

The use case for a database is not just large datasets, it’s large datasets of which only small parts change at a time.
And “large” means “more than 300 GB” on today’s servers.
I doubt that anybody is facing this kind of problem here.

Oh, and anybody who’s regularly updating 300 GB of data will have troubly putting that on disk, Hetzner’s servers can’t transfer more than 6 GB/s, which means you can’t transfer that much updated memory in less time than 50 seconds. If you need more throughput, you’re in multi-server land, and at that point, an in-memory database like SQLite won’t work anymore.

This all doesn’t mean that a database is useless, just that a database is least helpful when size becomes a concern.
From what I can see right now, for games, you want a database because you want to run ad-hoc queries. Mostly to find out what players are actually doing and aggregate that info to combat cheating, to balance the in-game economy, to check what features are actually being used, etc. It’s not the data size, it’s the SQL.
Just my perspective. Others might have found different use cases.

Well, I think that solves the problem. XD

14:28:31.799 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #68] from disk) took 4 millisecs. 14:28:31.806 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #193] from disk) took 7 millisecs. 14:28:31.822 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #346] from disk) took 16 millisecs. 14:28:31.825 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #14] from disk) took 3 millisecs. 14:28:31.835 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #345] from disk) took 10 millisecs. 14:28:31.838 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #194] from disk) took 3 millisecs. 14:28:31.870 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #17] from disk) took 32 millisecs. 14:28:31.883 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #81] from disk) took 13 millisecs. 14:28:31.910 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #80] from disk) took 27 millisecs. 14:28:31.921 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #332] from disk) took 11 millisecs. 14:28:31.930 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #102] from disk) took 9 millisecs. 14:28:31.932 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #100] from disk) took 2 millisecs. 14:28:31.934 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #518] from disk) took 2 millisecs. 14:28:31.937 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #101] from disk) took 3 millisecs. 14:28:31.941 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #175] from disk) took 4 millisecs. 14:28:31.944 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #38] from disk) took 3 millisecs. 14:28:31.949 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #99] from disk) took 5 millisecs. 14:28:31.956 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #39] from disk) took 7 millisecs. 14:28:31.962 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #514] from disk) took 6 millisecs. 14:28:31.971 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #311] from disk) took 9 millisecs. 14:28:31.980 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #36] from disk) took 9 millisecs. 14:28:31.988 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #37] from disk) took 7 millisecs. 14:28:31.991 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #495] from disk) took 4 millisecs. 14:28:31.994 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #190] from disk) took 3 millisecs. 14:28:32.010 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #491] from disk) took 16 millisecs. 14:28:32.015 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #189] from disk) took 5 millisecs. 14:28:32.017 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #179] from disk) took 2 millisecs. 14:28:32.034 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #176] from disk) took 17 millisecs. 14:28:32.036 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #298] from disk) took 2 millisecs. 14:28:32.050 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #299] from disk) took 14 millisecs. 14:28:32.055 [Game Thread] DEBUG c.m.g.d.g.galaxyoctree.LeafCache - Part 1 of cacheLeafs (loading [leaf #180] from disk) took 5 millisecs. Total time cacheLeafs took 260 millisecs.

MUCH appreciated guys! The funniest is, I remember using Buffered/Input(Output) at one point, but I don’t remember why I removed/changed it.

2 Likes