ABSTRACT

Bad Solutions  There are many ways to address these problems. One popular old-school way to improve the disk seeks between files is to log out all the file requests and rearrange the file layout on the final media so that seeks are always forward on the disk. CD-ROM and DVD drives typically perform seeks forward more quickly than backward, so this is a solution that only partially addresses the heart

of the problem and does nothing to handle the time wasted processing the data after each load occurs. In fact, loading individual files encourages a singlethreaded mentality that not only hurts performance but does not scale well with modern multithreaded development. The next iteration is to combine all the files into a giant metafile for a level, retaining a familiar file access interface, like the FILE type, fopen() function, and so on, but adding a large read-ahead buffer. This helps cut down further on the bandwidth stalls, but again, suffers from a single-threaded mentality when processing data, particularly when certain files contain other filenames that need to be queued up for reading. This spider web of dependencies exacerbates the optimization of file I/O. The next iteration in a system like this is to make it multithreaded. This basically requires some accounting mechanism using threads and callbacks. In this system, the order of operations cannot be assured because threads may be executed in any order, and some data processing occurs faster for some items than others. While this does indeed allow for continuous streaming in parallel with the loaded data initialization, it also requires a far more complicated scheme of accounting for objects that have been created but are not yet “live” in the game because they depend on other objects that are not yet live. In the end, there is a single object called a level that has explicit dependencies on all the subelements, and they on their subelements, recursively, which is allowed to become live only after everything is loaded and initialized. This undertaking requires clever management of reference counts, completion callbacks, initialization threads, and a lot of implicit dependencies that have to be turned into explicit dependencies.