paternostrox
paternostrox

Reputation: 445

Fastest way to save/load enum[] (byte) to a file

The array has duplicate elements and their order is important (must be kept). I have to save/load hundreds of these files constantly and each file may hold an array up to 100,000 elements.

The code bellow is an example of what I'm currently doing to save/load the files. Since IO is slow I got a significant speed improvement by casting the enums to byte before serialization (reducing the file size by 10x). I'm not sure I should be using BinaryFormatter though.

I'm still looking for improvements as everything should be as quick as possible, is there a better alternative to what I'm currently doing? How would you do it?

enum DogBreed : byte { Bulldog, Poodle, Beagle, Rottweiler, Chihuahua }

DogBreed[] myDogs = { DogBreed.Beagle, DogBreed.Poodle, DogBreed.Beagle, DogBreed.Bulldog };

public void Save(string path)
{
    BinaryFormatter formatter = new BinaryFormatter();
    FileStream stream = new FileStream(path, FileMode.Create);
    byte[] myDogsInByte = Array.ConvertAll(myDogs, new Converter<DogBreed, byte>(DogBreedToByte));
    formatter.Serialize(stream, myDogsInByte);
    stream.Close();
}

public bool Load(string path)
{
    if (!File.Exists(path))
    {
        return false;
    }

    BinaryFormatter formatter = new BinaryFormatter();
    FileStream stream = new FileStream(path, FileMode.Open);
    byte[] myDogsInByte = formatter.Deserialize(stream) as byte[];
    myDogs = Array.ConvertAll(myDogsInByte, new Converter<byte, DogBreed>(ByteToDogBreed));
    stream.Close();
    return true;
}

private byte DogBreedToByte(DogBreed db)
{
    return (byte)db;
}

private DogBreed ByteToDogBreed(byte bt)
{
    return (DogBreed)bt;
}

EDIT: New code based on Jeremy suggestion, the code is working, I'll try to test the performance of it and post the results here as soon as I can.

enum DogBreed : byte { Bulldog, Poodle, Beagle, Rottweiler, Chihuahua }

DogBreed[] myDogs = { DogBreed.Beagle, DogBreed.Poodle, DogBreed.Beagle, DogBreed.Bulldog };

public void Save(string path)
{       
    byte[] myDogsInByte = new byte[myDogs.Length];
    Array.Copy(myDogs,myDogsInByte,myDogs.Length);
    File.WriteAllBytes(path, myDogsInByte);
}

public bool Load(string path)
{
    if (!File.Exists(path))
    {
        return false;
    }

    byte[] myDogsInByte = File.ReadAllBytes(path);
    myDogs = (DogBreed[])(object)myDogsInByte;
    return true;
}

Upvotes: 1

Views: 332

Answers (1)

Jeremy Lakeman
Jeremy Lakeman

Reputation: 11110

While the C# compiler will complain if you attempt to directly assign a byte[] to an enum array. The runtime doesn't care.

var bytes = File.ReadAllBytes(path);
myDogs = (DogBreed[])(object)bytes;

The VS debugger will show that myDogs is really a byte array, but accessing an element from the array works just fine.

Update;

ArgumentException: Object must be an array of primitives.

So File.WriteAllBytes() doesn't like being tricked with an enum[]. You should be able to to use Array.Copy to quickly duplicate the enum values into a byte[].

   var buffer = new byte[myDogs.Length];
   Array.Copy(myDogs, buffer, myDogs.Length);
   File.WriteAllBytes(path, buffer);

Of course that's not a free operation, but it should be fairly fast even for large arrays.

Upvotes: 2

Related Questions