首页 > 技术文章 > C#调用C++ memcpy实现各种参数类型的内存拷贝 VS marshal.copy的实现 效率对比

timeObjserver 2017-06-17 18:13 原文

using System;
using System.Runtime.InteropServices;
using System.IO;
namespace tx
{
    struct ST
    {
       public char c1;
       public int x;
       public int y;
    }
    class Ct
    {
        [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
        public static extern void MemCopy(byte[] dest, byte[] src, int count);//字节数组到字节数组的拷贝

        [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
        public static extern void MemCopy(int[] dest, byte[] src, int count);//字节数组到整形数组的拷贝

        [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
        public unsafe static extern void MemCopy(ref ST dest, byte[] src, int count);//注意只有结构体能这么做,class不可以

        static void Main(string[] args)
        {
            //测试----------------------------------------------
            var ms = new MemoryStream();
            BinaryWriter writer = new BinaryWriter(ms);
            writer.Write((byte)'a');
            writer.Write((byte)'b');
            writer.Write((byte)'c');
            writer.Write((byte)'d');
            writer.Write((Int32)10);
            writer.Write((Int32)30);
            var len = ms.Length;
            int[] bs = new int[len/4];
            byte[] bss = new byte[len];

            byte[] buf = ms.GetBuffer();
            var ot = new ST();
            MemCopy(bs, buf, (int)len);
            MemCopy(bss, buf, (int)len);
            MemCopy(ref ot, buf, (int)len);//注意只有结构体能这么做,class不可以
        }

    }

}

Marshal对应的实现ByteToStruct,及效率对比完整程序如下:以读取魔兽世界M2文件为例,经测试发现ByteToStruct用时为MemCopy的3倍到4倍

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;
using NUnit.Framework.Internal.Filters;
using System;
using System.Runtime.InteropServices;
using System.Diagnostics;

using Debug = UnityEngine.Debug;
using System.Threading;
using System.ComponentModel;

struct Sphere
{
    /*0x00*/public float xmin, ymin, zmin;
    /*0x0C*/public float xmax, ymax, zmax;
    /*0x18*/public float radius;
};

struct ModelHeader {
    public byte id0, id1, id2, id3;
    public byte ver0, ver1, ver2, ver3;
    public UInt32 nameLength;
    public UInt32 nameOfs;
    public UInt32 GlobalModelFlags; // 1: tilt x, 2: tilt y, 4:, 8: add another field in header, 16: ; (no other flags as of 3.1.1);

    public UInt32 nGlobalSequences; // AnimationRelated
    public UInt32 ofsGlobalSequences; // A list of timestamps.
    public UInt32 nAnimations; // AnimationRelated
    public UInt32 ofsAnimations; // Information about the animations in the model.
    public UInt32 nAnimationLookup; // AnimationRelated
    public UInt32 ofsAnimationLookup; // Mapping of global IDs to the entries in the Animation sequences block.
    //UInt32 nD;
    //UInt32 ofsD;
    public UInt32 nBones; // BonesAndLookups
    public UInt32 ofsBones; // Information about the bones in this model.
    public UInt32 nKeyBoneLookup; // BonesAndLookups
    public UInt32 ofsKeyBoneLookup; // Lookup table for key skeletal bones.

    public UInt32 nVertices; // GeometryAndRendering
    public UInt32 ofsVertices; // Vertices of the model.
    public UInt32 nViews; // GeometryAndRendering
    //UInt32 ofsViews; // Views (LOD) are now in .skins.

    public UInt32 nColors; // ColorsAndTransparency
    public UInt32 ofsColors; // Color definitions.

    public UInt32 nTextures; // TextureAndTheifAnimation
    public UInt32 ofsTextures; // Textures of this model.

    public UInt32 nTransparency; // H,  ColorsAndTransparency
    public UInt32 ofsTransparency; // Transparency of textures.
    //UInt32 nI;   // always unused ?
    //UInt32 ofsI;
    public UInt32 nTexAnims;    // J, TextureAndTheifAnimation
    public UInt32 ofsTexAnims;
    public UInt32 nTexReplace; // TextureAndTheifAnimation
    public UInt32 ofsTexReplace; // Replaceable Textures.

    public UInt32 nTexFlags; // Render Flags
    public UInt32 ofsTexFlags; // Blending modes / render flags.
    public UInt32 nBoneLookup; // BonesAndLookups
    public UInt32 ofsBoneLookup; // A bone lookup table.

    public UInt32 nTexLookup; // TextureAndTheifAnimation
    public UInt32 ofsTexLookup; // The same for textures.

    public UInt32 nTexUnitLookup;        // L, TextureAndTheifAnimation, seems gone after Cataclysm
    public UInt32 ofsTexUnitLookup; // And texture units. Somewhere they have to be too.
    public UInt32 nTransparencyLookup; // M, ColorsAndTransparency
    public UInt32 ofsTransparencyLookup; // Everything needs its lookup. Here are the transparencies.
    public UInt32 nTexAnimLookup; // TextureAndTheifAnimation
    public UInt32 ofsTexAnimLookup; // Wait. Do we have animated Textures? Wasn't ofsTexAnims deleted? oO

    public Sphere collisionSphere;
    public Sphere boundSphere;

    public UInt32 nBoundingTriangles; // Miscellaneous
    public UInt32 ofsBoundingTriangles;
    public UInt32 nBoundingVertices; // Miscellaneous
    public UInt32 ofsBoundingVertices;
    public UInt32 nBoundingNormals; // Miscellaneous
    public UInt32 ofsBoundingNormals;

    public UInt32 nAttachments; // O, Miscellaneous
    public UInt32 ofsAttachments; // Attachments are for weapons etc.
    public UInt32 nAttachLookup; // P, Miscellaneous
    public UInt32 ofsAttachLookup; // Of course with a lookup.
    public UInt32 nEvents; //
    public UInt32 ofsEvents; // Used for playing sounds when dying and a lot else.
    public UInt32 nLights; // R
    public UInt32 ofsLights; // Lights are mainly used in loginscreens but in wands and some doodads too.
    public UInt32 nCameras; // S, Miscellaneous
    public UInt32 ofsCameras; // The cameras are present in most models for having a model in the Character-Tab.
    public UInt32 nCameraLookup; // Miscellaneous
    public UInt32 ofsCameraLookup; // And lookup-time again, unit16
    public UInt32 nRibbonEmitters; // U, Effects
    public UInt32 ofsRibbonEmitters; // Things swirling around. See the CoT-entrance for light-trails.
    public UInt32 nParticleEmitters; // V, Effects
    public UInt32 ofsParticleEmitters; // Spells and weapons, doodads and loginscreens use them. Blood dripping of a blade? Particles.
    public UInt32 nUnknown; // Apparently added in models with the 8-flag only. If that flag is not set, this field does not exist!
    public UInt32 ofsUnknown; // An array of shorts, related to renderflags.
};


public class m2 : MonoBehaviour {

    [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
    public static extern void MemCopy(byte[] dest, byte[] src, int count);//字节数组到字节数组的拷贝

    [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
    public static extern void MemCopy(int[] dest, byte[] src, int count);//字节数组到整形数组的拷贝

    [DllImport("msvcrt.dll", EntryPoint = "memcpy", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
    static extern void MemCopy(ref ModelHeader dest, byte[] src, int count);//注意只有结构体能这么做,class不可以


    // Use this for initialization
    void Start () {
        LoadMesh ("Assets/models/creature/arcticcondor.m2");
    }
    
    // Update is called once per frame
    void Update () {
        
    }

#region public 
    public UInt32[] globalSequences;

#endregion

#region private
    void LoadMesh(string filePath){
        if(false == File.Exists(filePath)){
            Debug.LogError ("file not exist : " + filePath);
            return;
        }

        var bytes = File.ReadAllBytes (filePath);

        var buffer = new BufferedStream (new MemoryStream (bytes));

        var wt = new Stopwatch ();
        wt.Start ();

        var theader = new ModelHeader ();
        var size_header = Marshal.SizeOf (theader);
        MemCopy (ref theader, bytes, size_header);

        Debug.Log ("MemCopy------------------------" + wt.Elapsed);
        wt.Stop ();

        var wt2 = new Stopwatch ();
        wt2.Start ();
        var header = (ModelHeader)ByteToStruct (bytes, typeof(ModelHeader));

        Debug.Log ("ByteToStruct------------------------" + wt2.Elapsed);
        wt2.Stop ();

        if(header.id0 != 'M' || header.id1 != 'D' || header.id2 != '2' || header.id3 != '0' ){
            Debug.LogError ("read m2 header : invalid id0, id1, id2, id3");
            return;
        }

        if(header.nameOfs != 304 && header.nameOfs != 320){
            Debug.LogError ("Invalid model nameOfs=" + header.nameOfs);
            return;
        }

        // Error check
        // 10 1 0 0 = WoW 5.0 models (as of 15464)
        // 10 1 0 0 = WoW 4.0.0.12319 models
        // 9 1 0 0 = WoW 4.0 models
        // 8 1 0 0 = WoW 3.0 models
        // 4 1 0 0 = WoW 2.0 models
        // 0 1 0 0 = WoW 1.0 models

        if(bytes.Length < header.ofsParticleEmitters){
            Debug.LogError ("unable to load the model \"" + filePath);
            return;
        }

        if(header.nGlobalSequences > 0){
            globalSequences = new UInt32[header.nGlobalSequences];

        }

    }

    void RenderMesh(){
        
    }
        
    public static object ByteToStruct(byte[] bytes, Type type)
    {
        int size = Marshal.SizeOf(type);
        if (size > bytes.Length)
        {
            return null;
        }
        //分配结构体内存空间
        IntPtr structPtr = Marshal.AllocHGlobal(size);
        //将byte数组拷贝到分配好的内存空间
        Marshal.Copy(bytes, 0, structPtr, size);
        //将内存空间转换为目标结构体
        object obj = Marshal.PtrToStructure(structPtr, type);
        //释放内存空间
        Marshal.FreeHGlobal(structPtr);
        return obj;
    }


#endregion

}

 

推荐阅读