Help with Neural Networks and Threading

Hi everyone, I’ve been working on an Artificial Life simulator of late as a sort of pet project. I’ve been working on building neural networks for the creatures brains, networks that can take in inputs such as raycasts or colliders and output values to “jets” or other force applicators. The neural network seems to work well enough, but my biggest problem is in optimizing them. Put simply, it takes too long, and even as few as five creatures lags my machine so much its unplayable. Even neural networks with as few as three neurons do this.

In an attempt to speed up the processing time, I’ve been working on a solution that involves multithreading; the code is below. I used [this][1] answer to build the thread code, but I am seeing no improvements in performance or the number of creatures I can have on the screen. I’m looking for any tips on optimization and threading. I’m mostly self-taught so if my documentation is bad please let me know and I’ll clean it up. Thanks!

Thread class:
public class ThreadedJob {

    private bool m_IsDone = false;
    private object m_Handle = new object();
    private System.Threading.Thread m_Thread = null;

    public bool IsDone
    {
      
        get
        {
            bool tmp;
            lock (m_Handle)
            {
                tmp = m_IsDone;
            }
            return tmp;
        }
        set
        {
            lock (m_Handle)
            {
                m_IsDone = value;
            }
        }
    }
    public virtual void Start()
    {
        m_Thread = new System.Threading.Thread(Run);
        m_Thread.Start();
    }
    public virtual void Abort()
    {
        m_Thread.Abort();
    }
    protected virtual void ThreadFunction() { }
    protected virtual void OnFinished() { }
    public virtual bool Update()
    {
        if (IsDone)
        {
            OnFinished();
            return true;
        }
        return false;
    }
    IEnumerator WaitFor()
    {
        while (!Update())
        {
            yield return null;
        }
    }
    private void Run()
    {
        ThreadFunction();
        IsDone = true;
    }
}

Neural net handler class (to interact with Unity API):

public class BrainInterface : MonoBehaviour
{

    EyeControl[] eyes;
    JetControl[] jets;
    Brain brain;
    public float[] inputValues;
    public float[] outputValues;
    public float[] weights;

    int inputNum, layerNum, neuronPerLayer, outputNum;

    void Start()
    {
        Birth birth = gameObject.GetComponent<Birth>();
        eyes = GetComponentsInChildren<EyeControl>();
        jets = GetComponentsInChildren<JetControl>();

        inputNum = eyes.Length;
        layerNum = 1;
        neuronPerLayer = 1;
        outputNum = jets.Length;
 //       Debug.Log("output number is " + outputNum);

        inputValues = new float[inputNum];
        for(int i = 0; i < inputNum; i++)
        {
            inputValues *= 0;*

}
outputValues = new float[outputNum];
for (int i = 0; i < outputNum; i++)
{
outputValues = 0;
}

weights = birth.arWeights;

brain = new Brain(inputNum, layerNum, neuronPerLayer, outputNum, weights);
brain.inputVal = inputValues;
foreach(float f in inputValues)
{
Debug.Log(f);
}
brain.Start();

}

void Update()
{
// Debug.Log(“reached”);

if(brain != null)
{
// Debug.Log(“brain is thinking”);
if (brain.Update())
{
// Debug.Log(“completed one think sesh”);
brain.inputVal = inputValues;
outputValues = brain.output;
brain.Start();
}
}

for(int j = 0; j < jets.Length; j++)
{
jets[j].setSpeed(outputValues[j]);
}
}

Brain class (too big to put the whole thing so this is just the function that “thinks”)
protected override void ThreadFunction()
{
think();
}

public void think()
{
// Debug.Log(“in the think function”);
// Debug.Log(inputVal.Length);
for(int i = 0; i < inputVal.Length; i++)
{
// Debug.Log("inputting value of " + inputVal*);*
}

//get the new inputs and calculate the first layer
for(int neu = 0; neu < neuronsPerLayer; neu++)
{
// Debug.Log(“now calculaing inputs for " + neu + " neuron”);
for (int i = 0; i < inputVal.Length; i++)
{
midLayer[0, neu].sumInput(inputVal*, i);*
// yield return null;
}
midLayer[0, neu].calOutput();
midLayer[0, neu].clearInput();
// Debug.Log("neuron " + neu + " has an output of " + midLayer[0, neu].getOutput());
// yield return null;
}

//calculate all the middle layers (might be none)
for(int lay = 1; lay < layerNum; lay++)
{
// Debug.Log(“now on " + lay + " layer”);
for (int neu = 0; neu < neuronsPerLayer; neu++)
{
// Debug.Log("working on neuron " + neu);
for(int oP = 0; oP < neuronsPerLayer; oP++)
{
midLayer[lay, neu].sumInput(midLayer[lay - 1, oP].getOutput(), oP);
// yield return null;
}
midLayer[lay, neu].calOutput();
midLayer[lay, neu].clearInput();
// Debug.Log("neuron " + neu + " has an output of " + midLayer[lay, neu].getOutput());
// yield return null;
}
}

//calculate the outputs
// Debug.Log(“now calculating the outputs”);

for(int neu = 0; neu < outputNum; neu++)
{
for(int oP = 0; oP< neuronsPerLayer; oP++)
{
outLayer[neu].sumInput(midLayer[layerNum - 1, oP].getOutput(), oP);
// yield return null;
}
outLayer[neu].calOutput();
outLayer[neu].clearInput();
Debug.Log("neuron " + neu + " has an output of " + outLayer[neu].getOutput());
output[neu] = outLayer[neu].getOutput();

}

// Debug.Log(“at the end of the thought”);

}
Thanks in advance for your time!
_*[1]: http://answers.unity3d.com/questions/357033/unity3d-and-c-coroutines-vs-threading.html*_

Your neural network code most likely is not the bottleneck, especially with when you don’t have large networks. Someone else recently had problems with an implementation and i created a NeuronalNetwork class. It does not use backpropagation or other learning methods, only a generational approach with random mutations. I can have at least 100+ objects and each has a net with 3 inputs, one middle layer with 5 neurons and 2 output neurons.

Make sure you remove ALL Debug.Logs from your code that is called frequently. Debug.Log is extremely slow. Especially inside the editor. In a build it’s way better but still quite an impact. Not to mention that it will blow up your logfiles into several MBs very quickly.

Only use Debug.Log when you actually debug something or if you just want to log some initial values. You should only use it temporally in Update / frequently called code.


edit
Just opened my old test project and run a short test:

As you can see i have about 260 objects there and run on 380+ fps even there are many Debug.DrawLines and TrailRenderers. Also note that i run the neural network code in Update and not on a seperate thread. Doing one run through a network is actually so little work that using threads most likely won’t improve anything. Actually having a lot tiny threads often do more harm then good. You also seem to use the “ThreadedJob” class, however it was ment for heavy one time tasks. Creating a new thread each frame is a very bad idea ^^. It creates tons of garbage and has a lot of overhead. The overhead is probably larger than the actual code ^^.

If you still have performance problems, try using the profiler in Unity to see where’s the bottleneck.

ps: I was actually wrong ^^ i used a 3-5-5-2 network. I’ve made an editor window to visualize a network: