Exemple #1
0
 public void Allocate(int numElement, DataType dataType, int alignment, Allocator allocator)
 {
     m_ReadFence  = new JobHandle();
     m_WriteFence = new JobHandle();
     elementCount = numElement;
     elementSize  = BarracudaArray.DataItemSize(dataType);
     type         = dataType;
     Assert.IsTrue(data == null, "Please call ClearState() when freeing underlying memory.");
     Assert.IsTrue(alignment % elementSize == 0);
     data = UnsafeUtility.Malloc(elementCount * elementSize, alignment, allocator);
     Assert.IsTrue(data != null);
 }
        /// <summary>
        /// Save model to file
        /// </summary>
        /// <param name="writer">`BinaryWriter`</param>
        /// <param name="model">`Model`</param>
        /// <param name="verbose">verbose flag</param>
        public static void Save(BinaryWriter writer, Model model, bool verbose = false)
        {
            Profiler.BeginSample("Barracuda.ModelWriter.Save");

            writer.Write((long)Model.Version);

            writer.Write(model.inputs.Count);
            for (var i = 0; i < model.inputs.Count; ++i)
            {
                WriteString(writer, model.inputs[i].name);
                WriteInt32Array(writer, model.inputs[i].shape);
            }
            WriteStringArray(writer, model.outputs);

            writer.Write(model.memories.Count);
            for (var m = 0; m < model.memories.Count; ++m)
            {
                WriteInt32Array(writer, model.memories[m].shape.ToArray());
                WriteString(writer, model.memories[m].input);
                WriteString(writer, model.memories[m].output);
            }

            // Write layers
            long offsetFromModelStartToLayer = 0;

            writer.Write(model.layers.Count);
            for (var l = 0; l < model.layers.Count; ++l)
            {
                Layer layer = model.layers[l];
                WriteString(writer, layer.name);
                writer.Write((Int32)layer.type);
                writer.Write((Int32)layer.activation);
                writer.Write(0); //dummy 0 size array
                writer.Write(0); //dummy 0 size array
                WriteInt32Array(writer, layer.pad);
                WriteInt32Array(writer, layer.stride);
                WriteInt32Array(writer, layer.pool);
                writer.Write(layer.axis);
                writer.Write(layer.alpha);
                writer.Write(layer.beta);
                writer.Write(0); //dummy 0 size array

                WriteStringArray(writer, layer.inputs);

                long offsetFromLayerStart = 0;
                writer.Write(layer.datasets.Length);
                for (var i = 0; i < layer.datasets.Length; ++i)
                {
                    WriteString(writer, layer.datasets[i].name);
                    WriteInt32Array(writer, layer.datasets[i].shape.ToArray());
                    // Recalculate all offsets to be global inside the model
                    // this way weights can be stored in one block at the end of the file
                    Assert.AreEqual(offsetFromLayerStart, layer.datasets[i].offset - layer.datasets[0].offset);
                    writer.Write(offsetFromModelStartToLayer + offsetFromLayerStart);
                    writer.Write(layer.datasets[i].itemSizeInBytes);
                    writer.Write(layer.datasets[i].length);
                    offsetFromLayerStart += layer.datasets[i].length;
                }
                offsetFromModelStartToLayer += offsetFromLayerStart;

                if (verbose)
                {
                    D.Log("layer " + l + ", " + layer.name + " type: " + layer.type.ToString() +
                          ((layer.activation != Layer.Activation.None) ? " activation " + layer.activation : "") +
                          " tensors: " + layer.datasets.Length +
                          " inputs: " + String.Join(",", layer.inputs));
                }

                if (verbose)
                {
                    foreach (var t in layer.datasets)
                    {
                        D.Log("        Tensor: " + t.shape + " offset: " + t.offset + " len: " + t.length);
                    }
                }
            }

            //Version 20 introduce weights type but full model need to be in the same type. Per layer no supported yet.
            Assert.IsTrue(model.layers.Count >= 0);
            var weightsDataType = model.layers[0].weights.Type;
            var sizeOfDataItem  = BarracudaArray.DataItemSize(weightsDataType);

            writer.Write((int)weightsDataType);

            //Pad to 4 bytes
            long writerCurrentPosition = writer.BaseStream.Position;
            long paddingForAlignment   = Model.WeightsAlignment - (writerCurrentPosition % Model.WeightsAlignment);

            writer.Write(new byte[paddingForAlignment]);

            // Write tensor data
            for (var l = 0; l < model.layers.Count; ++l)
            {
                for (var d = 0; d < model.layers[l].datasets.Length; ++d)
                {
                    Assert.AreEqual(weightsDataType, model.layers[0].weights.Type);
                    byte[] dst = new byte[model.layers[l].datasets[d].length * sizeOfDataItem];
                    BarracudaArray.BlockCopy(model.layers[l].weights, (int)(model.layers[l].datasets[d].offset * sizeOfDataItem), dst, 0, dst.Length);
                    writer.Write(dst);
                }
            }

            WriteString(writer, model.IrSource);
            WriteString(writer, model.IrVersion);
            WriteString(writer, model.ProducerName);
            int numWarnings = model.Warnings.Count;

            writer.Write(numWarnings);
            for (var i = 0; i < numWarnings; ++i)
            {
                WriteString(writer, model.Warnings[i].LayerName);
                WriteString(writer, model.Warnings[i].Message);
            }

            int numMetadataProps = model.Metadata.Count;

            writer.Write(numMetadataProps);
            foreach (KeyValuePair <string, string> kvp in model.Metadata)
            {
                WriteString(writer, kvp.Key);
                WriteString(writer, kvp.Value);
            }

            Profiler.EndSample();
        }
Exemple #3
0
        private static BarracudaArray ReadLargeWeightArray(BinaryReader file, Int64 count, DataType dataType)
        {
            int   bytesToRead;
            Int64 bytesToReadInt64 = count * BarracudaArray.DataItemSize(dataType);

            try
            {
                bytesToRead = Convert.ToInt32(bytesToReadInt64); // throws OverflowException
            }
            catch (OverflowException)
            {
                throw new OverflowException($"Files larger than 2GB currently are not supported. Attempt to read {bytesToReadInt64} bytes.");
            }

            //1-Try to remap byte[] stream to avoid allocation
            Profiler.BeginSample("Barracuda.RemapWeights");
            BarracudaArray remappedWeights = null;

            try
            {
                Stream stream          = file.BaseStream;
                var    memoryStream    = stream as MemoryStream;
                var    sourceBuffer    = memoryStream?.GetBuffer();
                int    currentPosition = (int)memoryStream?.Position;
                remappedWeights = new BarracudaArrayFromManagedArray(sourceBuffer, currentPosition, dataType, (int)count);
            }
        #if UNITY_EDITOR
            catch (InvalidOperationException e)
            {
                UnityEngine.Debug.Log("ModelLoader: Can't remap memory stream to underlying data type, allocation and copy will occurs. Exception: " + e);
            }
        #else
            catch (InvalidOperationException) {}
        #endif
            if (remappedWeights != null)
            {
                //We remapped memory. Need to advance stream position to be consistent with read behavior.
                file.BaseStream.Position += bytesToRead;
                Profiler.EndSample();
                return(remappedWeights);
            }
            Profiler.EndSample();

            //2-Can't remap will copy from managed memory to native
            Profiler.BeginSample("Barracuda.AllocWeights");
            BarracudaArray loadedWeights = new BarracudaArray((int)count, dataType);
            Profiler.EndSample();

            Profiler.BeginSample("Barracuda.LoadWeights");
            try
            {
                var readBuffer = new byte[4096]; // 4Kb is close to optimal read size.
                                                 // See for measurements: https://www.jacksondunstan.com/articles/3568
                                                 // Read size vs relative read-time:
                                                 // 64b: x10, 128b: x6, 256b: x4, 1Kb: x3, 4Kb: x3
                int writeOffset = 0;
                while (writeOffset < bytesToRead)
                {
                    var bytesLeftToRead = bytesToRead - writeOffset;
                    var readSizeInBytes = Math.Min(readBuffer.Length, bytesLeftToRead);

                    Assert.IsTrue(readSizeInBytes > 0);
                    Assert.IsTrue(readSizeInBytes <= readBuffer.Length);
                    readSizeInBytes = file.BaseStream.Read(readBuffer, offset: 0, count: readSizeInBytes);
                    if (readSizeInBytes == 0)
                    {
                        throw new IOException($"Unexpected EOF reached. Read {writeOffset / sizeof(float)} out of expected {count} floats before reaching end of file.");
                    }

                    BarracudaArray.BlockCopy(
                        sourceArray: readBuffer, sourceByteOffset: 0,
                        destinationArray: loadedWeights, destinationByteOffset: writeOffset,
                        lengthInBytes: readSizeInBytes);
                    writeOffset += readSizeInBytes;
                }
                Assert.AreEqual(writeOffset, bytesToRead);
            }
            finally
            {
                Profiler.EndSample();
            }

            return(loadedWeights);
        }
Exemple #4
0
        static IEnumerator LoadAsync(BinaryReader fileReader, Model model, bool verbose = true, bool applyPatching = true, bool skipWeights = false, float maxTimePerYield = 0f)
        {
            using (BinaryReader file = fileReader)
            {
                Profiler.BeginSample("Barracuda.LoadLayers");
                float timeStart = Time.realtimeSinceStartup;

                if (model == null)
                {
                    model = new Model();
                }
                List <Layer> layers = new List <Layer>();

                long version = file.ReadInt64() % 0xff; // magic
                if (version != Model.Version && version != Model.LastVersionWithout8DSupport && version != Model.LastVersionWithoutWeightsAlignmentSupport)
                {
                    throw new NotSupportedException($"Format version not supported: {version}");
                }

                var count = file.ReadInt32();
                model.inputs = new List <Model.Input>(count);
                for (var i = 0; i < count; ++i)
                {
                    model.inputs.Add(new Model.Input {
                        name = ReadString(file), shape = ReadInt32Array(file)
                    });

                    if (maxTimePerYield > 0 && Time.realtimeSinceStartup - timeStart > maxTimePerYield)
                    {
#if DEBUG_TIMING
                        UnityEngine.Debug.Log(Time.realtimeSinceStartup - timeStart);
#endif
                        yield return(null);

                        timeStart = Time.realtimeSinceStartup;
                    }
                }

                model.outputs = ReadStringArray(file).ToList();

                count          = file.ReadInt32();
                model.memories = new List <Model.Memory>(count);
                for (var m = 0; m < count; ++m)
                {
                    model.memories.Add(new Model.Memory
                    {
                        shape  = new TensorShape(ReadInt32Array(file)),
                        input  = ReadString(file),
                        output = ReadString(file)
                    });

                    if (maxTimePerYield > 0 && Time.realtimeSinceStartup - timeStart > maxTimePerYield)
                    {
#if DEBUG_TIMING
                        UnityEngine.Debug.Log(Time.realtimeSinceStartup - timeStart);
#endif
                        yield return(null);

                        timeStart = Time.realtimeSinceStartup;
                    }
                }

                int numberOfLayers = file.ReadInt32();
                for (var l = 0; l < numberOfLayers; ++l)
                {
                    var   name       = ReadString(file);
                    var   layerType  = (Layer.Type)file.ReadInt32();
                    var   activation = (Layer.Activation)file.ReadInt32();
                    Layer layer      = new Layer(name, layerType, activation);
                    ReadInt32Array(file);                   // dummy
                    ReadInt32Array(file);                   // dummy
                    layer.pad    = ReadInt32Array(file);
                    layer.stride = ReadInt32Array(file);
                    layer.pool   = ReadInt32Array(file);
                    layer.axis   = ConvertLayerAxisFor8DShapeSupportIfNeeded(file.ReadInt32(), version, layerType);
                    layer.alpha  = file.ReadSingle();
                    layer.beta   = file.ReadSingle();
                    ReadInt32Array(file);                   // dummy

                    layer.inputs = ReadStringArray(file);

                    if (maxTimePerYield > 0 && Time.realtimeSinceStartup - timeStart > maxTimePerYield)
                    {
#if DEBUG_TIMING
                        UnityEngine.Debug.Log(Time.realtimeSinceStartup - timeStart);
#endif
                        yield return(null);

                        timeStart = Time.realtimeSinceStartup;
                    }

                    layer.datasets = new Layer.DataSet[file.ReadInt32()];
                    for (var i = 0; i < layer.datasets.Length; ++i)
                    {
                        if (maxTimePerYield > 0 && Time.realtimeSinceStartup - timeStart > maxTimePerYield)
                        {
#if DEBUG_TIMING
                            UnityEngine.Debug.Log(Time.realtimeSinceStartup - timeStart);
#endif
                            yield return(null);

                            timeStart = Time.realtimeSinceStartup;
                        }

                        layer.datasets[i].name            = ReadString(file);
                        layer.datasets[i].shape           = new TensorShape(ReadInt32Array(file));
                        layer.datasets[i].offset          = file.ReadInt64();
                        layer.datasets[i].itemSizeInBytes = file.ReadInt32();
                        layer.datasets[i].length          = file.ReadInt32();
                    }

                    layers.Add(layer);

                    if (verbose)
                    {
                        D.Log(
                            $"layer {l}, {layer.name} type: {layer.type} " +
                            $"{((layer.activation != Layer.Activation.None) ? $"activation {layer.activation} " : "")}" +
                            $"tensors: {layer.datasets.Length} inputs: {String.Join(",", layer.inputs)}");
                    }

                    if (verbose)
                    {
                        foreach (var t in layer.datasets)
                        {
                            D.Log($"        Tensor: {t.shape} offset: {t.offset} len: {t.length}");
                        }
                    }

                    if (applyPatching)
                    {
                        PatchLayer(layers, layer);
                    }

                    if (maxTimePerYield > 0 && Time.realtimeSinceStartup - timeStart > maxTimePerYield)
                    {
#if DEBUG_TIMING
                        UnityEngine.Debug.Log(Time.realtimeSinceStartup - timeStart + ": " + l);
#endif
                        yield return(null);

                        timeStart = Time.realtimeSinceStartup;
                    }
                }
                model.layers = layers;

                Int64 numWeightsToRead = 0;
                for (var l = 0; l < model.layers.Count; ++l)
                {
                    for (var d = 0; d < model.layers[l].datasets.Length; ++d)
                    {
                        numWeightsToRead += model.layers[l].datasets[d].length;

                        if (maxTimePerYield > 0 && Time.realtimeSinceStartup - timeStart > maxTimePerYield)
                        {
#if DEBUG_TIMING
                            UnityEngine.Debug.Log(Time.realtimeSinceStartup - timeStart);
#endif
                            yield return(null);

                            timeStart = Time.realtimeSinceStartup;
                        }
                    }
                }

                Profiler.EndSample();

                DataType weightsDataType = DataType.Float;
                if (version >= 20)
                {
                    //Version 20 introduce weights type but full model need to be in the same type. Per layer no supported yet.
                    weightsDataType = (DataType)file.ReadInt32();
                }

                if (version >= 19)
                {
                    //Padding so weights are aligned on Model.WeightsAlignment bytes
                    long streamCurrentPosition = file.BaseStream.Position;
                    long paddingForAlignment   = Model.WeightsAlignment - (streamCurrentPosition % Model.WeightsAlignment);
                    file.BaseStream.Seek(paddingForAlignment, SeekOrigin.Current);
                }

                if (skipWeights)
                {
                    SkipLargeByteArray(file, numWeightsToRead * BarracudaArray.DataItemSize(weightsDataType));
                }
                else
                {
                    if (maxTimePerYield > 0 && Time.realtimeSinceStartup - timeStart > maxTimePerYield)
                    {
#if DEBUG_TIMING
                        UnityEngine.Debug.Log(Time.realtimeSinceStartup - timeStart);
#endif
                        yield return(null);

                        timeStart = Time.realtimeSinceStartup;
                    }

                    var sharedWeightsArray = ReadLargeWeightArray(file, numWeightsToRead, weightsDataType);

                    Assert.AreEqual(weightsDataType, sharedWeightsArray.Type);
                    for (var l = 0; l < model.layers.Count; ++l)
                    {
                        model.layers[l].weights = sharedWeightsArray;

                        if (maxTimePerYield > 0 && Time.realtimeSinceStartup - timeStart > maxTimePerYield)
                        {
#if DEBUG_TIMING
                            UnityEngine.Debug.Log(Time.realtimeSinceStartup - timeStart);
#endif
                            yield return(null);

                            timeStart = Time.realtimeSinceStartup;
                        }
                    }
                }

                // Importer Reporting
                try
                {
                    model.IrSource     = ReadString(file);
                    model.IrVersion    = ReadString(file);
                    model.ProducerName = ReadString(file);
                    int numWarnings = file.ReadInt32();
                    for (var i = 0; i < numWarnings; ++i)
                    {
                        model.Warnings.Add(new Model.ImporterWarning(ReadString(file), ReadString(file)));
                    }

                    if (version >= 18)
                    {
                        int numMetadataProps = file.ReadInt32();
                        for (var i = 0; i < numMetadataProps; ++i)
                        {
                            model.Metadata.Add(ReadString(file), ReadString(file));
                        }
                    }
                }
                catch (EndOfStreamException)
                {
                    //Do nothing Importer Reporting data might not be present for backward compatibility reasons
                }

                yield return(model);
            }
        }