본문 바로가기

Unity

유니티에서 테이블 데이터 로드하기 #3 - Marshal

System.Runtime.InterOpServices.Marshal을 사용하여 byte array로 변환하는 방법도 고려해볼 수 있다.

이 방법은 특히 관리되지 않는 형식, 즉 int, float 등으로만 구성된 클래스를 저장하고 읽을 때 빠른 속도를 보여준다. 데이터의 형식에 따라 상당히 달라질 수 있지만 json보다도 3~5배가량의 속도 향상을 기대해볼 수 있다.

 

Marshal은 프로그램이 다른 프로그램에서 사용할 수 있도록 데이터를 전송하는 것을 의미하는데, Marshal을 하려면 직렬화를 하는 경우가 많다. 따라서 .NET에서는 Marshal.StructureToPtr과 Marshal.PtrToStructure를 이용해 간단히 클래스를 byte array로 직렬화하는 메소드를 제공한다.

 

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;


[StructLayout(LayoutKind.Sequential, Pack = 1)]
public class Row
{
    public int A;
    public float B;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)]
    public string C;
}

public class Data
{
    public List<Row> Rows;
}

public static class Testbed
{
    public static Data LoadBytes(byte[] bytes, int rowCount)
    {
        var rowSize = Marshal.SizeOf(new Row());
        IntPtr buffer = Marshal.AllocHGlobal(rowSize);

        var dataSet = new Data();

        for (var i = 0; i < rowCount; i++)
        {
            Marshal.Copy(bytes, i * rowSize, buffer, rowSize);
            dataSet.Rows.Add(Marshal.PtrToStructure<Row>(buffer));
        }

        Marshal.FreeHGlobal(buffer);

        return dataSet;
    }
}

 

개인적으로 Marshal을 사용하여 직렬화 하는 것은 추천하지는 않는 방법이다. 

그냥 가능은 하다 정도로 생각하는게 좋을 것 같다.

 

이유는 여러가지가 있는데, 일단 .NET Marshal이 애초에 대량의 데이터를 직렬화하려고 만든것이 아니기 때문에 제약도 많고, API 디자인도 적합하지 않다. 예를 들어 List<T> 등을 직접 직렬화 할 수 없고, PtrToStucture가 이름에 걸맞지 않게 struct에는 사용할 수 없다는 점, 

 

또한 여러가지 제약이 붙는데, 필드간의 순서를 고정하기 위해 [StructLayout(LayoutKind.Sequential, Pack = 1)] 특성이 필요하며, string은 관리되는 형식이니만큼 직접 직렬화할 수 없다. 다행히 .NET에는 string을 고정 길이 바이트 배열로 직렬화하는 기능이 내장되어 있지만, [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 50)] 같은 특성을 string마다 덕지덕지 붙여야 하며 고정길이를 넘어가지 않는지도 신경써야 하고, 만약 길이를 넉넉하게 잡는다면 엄청난 양의 디스크 및 메모리가 낭비될 수 있다.

 

 


장점

  1. 빠르다.
  2. 비교적 적은 코드 수정으로 속도를 크게 끌어올릴 수 있다..

단점

  1. 문자열 저장이 비효율적이다.
  2. 이런저런 특성을 덕지덕지 붙여야 한다.
  3. 타입으로 struct를 사용할 수 없다.
  4. List/Array를 통째로 저장하고 불러오는 로직은 직접 만들어야 한다. (첫 4바이트를 행의 갯수를 저장하는데 쓴다던가)