c# - 请求分块加载我的文件的方向(只需要建议)

标签 c# .net memory-management memory-mapped-files chunks

过去几天,我一直在研究一种方法,可以在保存时将我的 xna 游戏的 1.44 亿个图 block 表示压缩到非常小的尺寸。成功完成后,我现在发现自己对如何从文件中分块取回它们感到困惑。

在我的文件中。

  1. 一个整数(使用 7BitEncodedInt 方法将其压缩为字节)
  2. 一个字节

压缩后的整数表示瓦片的数量,后面的字节决定瓦片的类型。这一切都很好,而且效果很好。最重要的是,它将文件大小平均缩小到仅 50mb。

问题是我目前正在读回整个文件。 我从文件中得到这个。

  1. 每个图 block 的索引值(只是我抓取图 block 时的基本迭代)
  2. 每个图 block 的类型作为字节值
  3. 表示该图 block 纹理的字节值(这很难解释,但对每个图 block 而言都是必需的)

所有这一切的最终结果是我设法保存了文件并且只使用了大约 50mb。但是通过将整个东西加载回去,它在 ram 上扩展到近 1.5gigs。我真的不能再牺牲磁贴信息了。所以我需要一种方法来根据玩家位置只加载 map 的一部分。目标是在 100-200mb 范围内

我一直在研究文件的内存映射,使用四叉树,几乎所有我能找到的用于分块加载文件的东西。虽然这些选项看起来都不错,但我不确定哪个是最好的,或者如果考虑到这种情况,可能还有另一个更好的选项。所有这一切的另一个问题是,这些解决方案似乎都非常复杂(尤其是因为这是我第一次使用它们),虽然我不反对将自己投入到一些冗长的编码中,但我想知道它会做我想做的事事先需要它。

我的问题是,考虑到我在拉入文件时必须如何处理文件,以及需要根据玩家位置完成此操作的事实,最好的方法是什么?我只是在这里寻找一些方向。代码总是受欢迎的,但不是必需的。

最佳答案

您想在 Tile 类中使用固定长度的变量并实现如下内容:

这是一个集合类 (People) 的示例,它可以根据索引从序列化到文件中的集合中获取值。

Person 是 People 集合的基础类。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FileStreamDatabaseTest
{
    class Program
    {
        static void Main(string[] args)
        {
            People.OpenCollection();
            People.Test_WillOverwriteData();
            People.CloseCollection();
            Console.ReadLine();
        }
    }

    public class Person
    {
        // define maxium variable sizes for serialisation
        protected static int pMaxLength_FirstName = 64;
        protected static int pMaxLength_Age = 10;
        public static int MaxObjectSize
        {
            get
            {
                // return the sum of all the maxlegnth variables to define the entire object size for serialisation
                return pMaxLength_FirstName + pMaxLength_Age;
            }
        }

        // define each object that will be serialised as follows 
        protected string pFirstName;
        public string Firstname
        {
            set
            {
                // ensure the new value is not over max variable size
                if (value.Length > pMaxLength_FirstName)
                    throw new Exception("the length of the value is to long.");

                pFirstName = value;
            }
            get
            {
                return pFirstName;
            }
        }
        protected int pAge;
        public int Age
        {
            get
            {
                return pAge;
            }
            set
            {
                pAge = value;
            }
        }

        public byte[] Serialise()
        {
            // Output string builder
            StringBuilder Output = new StringBuilder();

            // Append firstname value
            Output.Append(Firstname);

            // Add extra spaces to end of string until max length is reached
            if (Firstname.Length < pMaxLength_FirstName)
                for (int i = Firstname.Length; i < pMaxLength_FirstName; i++)
                    Output.Append(" ");

            // Append age value as string
            Output.Append(Age.ToString());

            // Add extra spaces to end of string until max length is reached
            int AgeLength = Age.ToString().Length;
            if (AgeLength < pMaxLength_Age)
                for (int i = AgeLength; i < pMaxLength_Age; i++)
                    Output.Append(" ");

            // Return the output string as bytes using ascii encoding
            return System.Text.Encoding.ASCII.GetBytes(Output.ToString());
        }

        public void Deserialise(byte[] SerialisedData)
        {
            string Values = System.Text.Encoding.ASCII.GetString(SerialisedData);

            pFirstName = Values.Substring(0, pMaxLength_FirstName).Trim();
            pAge = int.Parse(Values.Substring(pMaxLength_FirstName, pMaxLength_Age).Trim());
        }
    }

    public static class People
    {
        private static string tileDatasource = @"c:\test.dat";
        private static System.IO.FileStream FileStream;

        public static void OpenCollection()
        {
            FileStream = new System.IO.FileStream(tileDatasource, System.IO.FileMode.OpenOrCreate, System.IO.FileAccess.ReadWrite, System.IO.FileShare.None);
        }

        public static void CloseCollection()
        {
            FileStream.Close();
            FileStream.Dispose();
            FileStream = null;
        }

        public static void SaveCollection(Person[] People)
        {
            FileStream.SetLength(People.Length * Person.MaxObjectSize);
            FileStream.Position = 0;

            foreach (Person PersonToWrite in People)
            {
                // call serialise to get bytes
                byte[] OutputBytes = PersonToWrite.Serialise();

                // write the output buffer
                // note: this will always be the same size as each variable should 
                //       append spaces until its max size is reached
                FileStream.Write(OutputBytes, 0, OutputBytes.Length);
            }
        }

        public static Person GetValue(int Index)
        {
            // set the stream position to read the object by multiplying the requested index with the max object size
            FileStream.Position = Index * Person.MaxObjectSize;

            // read the data
            byte[] InputBytes = new byte[Person.MaxObjectSize];
            FileStream.Read(InputBytes, 0, Person.MaxObjectSize);

            // deserialise
            Person PersonToReturn = new Person();
            PersonToReturn.Deserialise(InputBytes);

            // retun the person
            return PersonToReturn;
        }

        public static void Test_WillOverwriteData()
        {
            long StartTime;
            long EndTime;
            TimeSpan TimeTaken;

            Console.WriteLine("-------------------------------------------------------------------");
            Console.WriteLine("*** Creating 2,000,000 test people... ");
            StartTime = DateTime.Now.Ticks;
            Person[] People = new Person[2000000];
            for (int i = 0; i < 2000000; i++)
            {
                People[i] = new Person();
                People[i].Firstname = "TestName." + i;
                People[i].Age = i;
            }
            EndTime = DateTime.Now.Ticks;
            TimeTaken = new TimeSpan(EndTime - StartTime);
            Console.WriteLine("-> Completed in " + TimeTaken.TotalSeconds + " seconds");

            Console.WriteLine("-------------------------------------------------------------------");
            Console.WriteLine("*** Serialising Collection to disk... ");
            StartTime = DateTime.Now.Ticks;
            SaveCollection(People);
            EndTime = DateTime.Now.Ticks;
            TimeTaken = new TimeSpan(EndTime - StartTime);
            Console.WriteLine("-> Completed in " + TimeTaken.TotalSeconds + " seconds");

            Console.WriteLine("-------------------------------------------------------------------");
            Console.WriteLine("*** Redundancy Test... ");
            StartTime = DateTime.Now.Ticks;
            bool Parsed = true;
            int FailedCount = 0;
            for (int i = 0; i < 2000000; i++)
            {
                if (GetValue(i).Age != i)
                {
                    Parsed = false;
                    FailedCount++;
                }
            }
            EndTime = DateTime.Now.Ticks;
            TimeTaken = new TimeSpan(EndTime - StartTime);
            Console.WriteLine("-> " + (Parsed ? "PARSED" : "FAILED (" + FailedCount + " failed index's"));
            Console.WriteLine("-> Completed in " + TimeTaken.TotalSeconds + " seconds");

            Console.WriteLine("-------------------------------------------------------------------");
            Console.WriteLine("*** Reading 10,000 index's at once... ");
            StartTime = DateTime.Now.Ticks;
            Person[] ChunkOfPeople = new Person[10000];
            for (int i = 0; i < 10000; i++)
                ChunkOfPeople[i] = GetValue(i);
            EndTime = DateTime.Now.Ticks;
            TimeTaken = new TimeSpan(EndTime - StartTime);
            Console.WriteLine("-> Completed in " + TimeTaken.TotalSeconds + " seconds");


            Console.WriteLine("-------------------------------------------------------------------");
            Console.WriteLine("*** Reading 100,000 index's at once... ");
            StartTime = DateTime.Now.Ticks;
            ChunkOfPeople = new Person[100000];
            for (int i = 0; i < 100000; i++)
                ChunkOfPeople[i] = GetValue(i);
            EndTime = DateTime.Now.Ticks;
            TimeTaken = new TimeSpan(EndTime - StartTime);
            Console.WriteLine("-> Completed in " + TimeTaken.TotalSeconds + " seconds");

            Console.WriteLine("-------------------------------------------------------------------");
            Console.WriteLine("*** Reading 1,000,000 index's at once... ");
            StartTime = DateTime.Now.Ticks;
            ChunkOfPeople = new Person[1000000];
            for (int i = 0; i < 1000000; i++)
                ChunkOfPeople[i] = GetValue(i);
            EndTime = DateTime.Now.Ticks;
            TimeTaken = new TimeSpan(EndTime - StartTime);
            Console.WriteLine("-> Completed in " + TimeTaken.TotalSeconds + " seconds");
        }
    }     
}

关于c# - 请求分块加载我的文件的方向(只需要建议),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6352816/

相关文章:

java - JVM 中裸 java.lang.Object 的实际大小

c++ - 结构的重新分配使程序崩溃

c# - 如何随机选择一种已知的控制台颜色作为文本?

c# - 如何定义一个表,其主键由 2 个外键构成,EF 代码优先

C# 比较 4 个字符串数组

c# - .NET 中的双倍乘法是否损坏?

c# - FtpWebRequest 下载文件

c# - .NET Remoting,将对象传递给方法

.net - linq to sql将这个查询翻译成: "select *"?格式是不是效率低下

c++ - 自定义堆分配器 : address of Derived need cached inside smart pointer?