gpt4 book ai didi

c# - 主线程上的网络请求导致帧率延迟峰值

转载 作者:行者123 更新时间:2023-12-04 17:15:21 25 4
gpt4 key购买 nike

我目前正在开展一个项目,使用 OpenAI 的 GPT3 为 NPC 对话提供支持。每个 NPC 在他们开始对话时向我的服务器发送一个 POST 请求,该请求返回一个音频文件,NPC 通过其本地 AudioSource 播放该文件。

视频在这里:https://www.youtube.com/watch?v=pygM6yDE9hI

我遇到的一个问题是抖动和短时滞后。

enter image description here

我是 Unity 的新手,但根据分析器,我认为罪魁祸首是处理网络请求的协程。

下面是我实现此功能的代码:

     // Update is called once per frame
void Update()
{
int index = -1;
if (conversation != null)
{
if (!conversation.processing)
{
if (conversation.currentSpeaker.Equals(this.id))
{
Debug.Log(this.id + " getting response");
conversation.processing = true;
StartCoroutine(getResponse(conversation));
}
}
}
else if (Datastore.Instance.id2conversation.TryGetValue(id, out index))
{
conversation = Datastore.Instance.conversations[index];
}
}

IEnumerator getResponse(Conversation conversation)
{
WWWForm form = new WWWForm();
form.AddField("id", this.id);
var www = UnityWebRequest.Post("http://" + Datastore.Instance.host + ":3000/generate", form);

yield return www.SendWebRequest();

if (interrupted) yield break;

if (www.isNetworkError)
{
Debug.Log(www.error);
}
else
{
if (www.GetResponseHeaders().Count > 0)
{
var jsonData = JSON.Parse(www.downloadHandler.text);

string stringData = jsonData["audioContent"]["data"].ToString();
byte[] rawdata = AudioHelpers.ConvertToByteStream(stringData);

AudioClip clip = AudioHelpers.ConvertToAudioClip(rawdata);

this.audioSource.clip = clip;
this.audioSource.Play();

this.animator.SetBool(this.talkingBoolHash, true);

Debug.Log("Response Recieved");

yield return new WaitForSeconds(clip.length);

if (interrupted) yield break;

this.conversation.currentSpeaker = jsonData["nextSpeaker"].ToString().Replace("\"", "");
this.conversation.processing = false;
this.animator.SetBool(this.talkingBoolHash, false);
}
}
}

我怎样才能提高此代码的性能以消除帧率下降的时期?

是否可以通过 Unity Jobs 将此代码移动到另一个线程?

如有任何帮助,我们将不胜感激。

编辑:深度剖析器: Deep Profiler View

实现了 JSON 解析的线程。新代码:

// Update is called once per frame
void Update()
{
int index = -1;
if (conversation != null)
{
if (!conversation.processing)
{
if (conversation.currentSpeaker.Equals(this.id))
{
Debug.Log(this.id + " getting response");
conversation.processing = true;
StartCoroutine(getResponse(conversation));
}
}
}
else if (Datastore.Instance.id2conversation.TryGetValue(id, out index))
{
conversation = Datastore.Instance.conversations[index];
}

}

Task<(byte[], string)> ParseAudioData(string rawJson)
{
try
{
return Task.Run(() => {
var jsonData = JSON.Parse(rawJson);
string stringData = jsonData["audioContent"]["data"].ToString();
byte[] rawdata = AudioHelpers.ConvertToByteStream(stringData);
string nextSpeaker = jsonData["nextSpeaker"].ToString().Replace("\"", "");
return Task.FromResult((rawdata, nextSpeaker));
});
}
catch(Exception e)
{
Debug.LogException(e);
throw;
}
}

IEnumerator getResponse(Conversation conversation)
{
WWWForm form = new WWWForm();
form.AddField("id", this.id);

var www = UnityWebRequest.Post("http://" + Datastore.Instance.host + ":3000/generate", form);

yield return www.SendWebRequest();

if (interrupted) yield break;

if (www.isNetworkError)
{
Debug.Log(www.error);
}
else
{
if (www.GetResponseHeaders().Count > 0)
{
Task<(byte[], string)> t = ParseAudioData(www.downloadHandler.text);

yield return t;

AudioClip clip = AudioHelpers.ConvertToAudioClip(t.Result.Item1);

this.audioSource.clip = clip;
this.audioSource.Play();

this.animator.SetBool(this.talkingBoolHash, true);

Debug.Log("Response Recieved");

yield return new WaitForSeconds(clip.length);

if (interrupted) yield break;

this.conversation.currentSpeaker = t.Result.Item2;
this.conversation.processing = false;
this.animator.SetBool(this.talkingBoolHash, false);
}
}
}

最终编辑:在下面的答案的帮助下它开始工作了!

我的最终代码:

Task<JSONNode> ParseJsonData(string rawJson)
{
try
{
return Task.Run(() =>
{
JSONNode jsonData = JSON.Parse(rawJson);
return jsonData;
});
}
catch (Exception e)
{
Debug.LogException(e);
throw;
}
}

Task<(byte[], string)> ParseAudioData(JSONNode jsonData)
{
try
{
return Task.Run(() => {
string stringData = jsonData["audioContent"]["data"].ToString();
byte[] rawdata = AudioHelpers.ConvertToByteStream(stringData);
string nextSpeaker = jsonData["nextSpeaker"].ToString().Replace("\"", "");
return (rawdata, nextSpeaker);
});
}
catch (Exception e)
{
Debug.LogException(e);
throw;
}
}
IEnumerator PlayDialog(AudioClip clip, string nextSpeaker)
{
this.audioSource.clip = clip;
this.audioSource.Play();
this.animator.SetBool(this.talkingBoolHash, true);

yield return new WaitForSeconds(clip.length);

if (interrupted) yield break;

this.conversation.currentSpeaker = nextSpeaker;
this.conversation.processing = false;
this.animator.SetBool(this.talkingBoolHash, false);
}

IEnumerator getResponse(Conversation conversation)
{
WWWForm form = new WWWForm();
form.AddField("id", this.id);
var www = UnityWebRequest.Post("http://" + Datastore.Instance.host + ":3000/generate", form);

yield return www.SendWebRequest();

if (interrupted) yield break;

if (www.isNetworkError)
{
Debug.Log(www.error);
}
else
{
if (www.GetResponseHeaders().Count > 0)
{
ParseJsonData(www.downloadHandler.text).ContinueWith((jsonData) => {
ParseAudioData(jsonData.Result).ContinueWith((t) =>
{
// https://github.com/PimDeWitte/UnityMainThreadDispatcher
UnityMainThreadDispatcher.Instance().Enqueue(() =>
{
AudioClip clip = AudioHelpers.ConvertToAudioClip(t.Result.Item1);
StartCoroutine(PlayDialog(clip, t.Result.Item2));
Debug.Log("Response Recieved");
});
});
});

}
}
}

最佳答案

我想说延迟峰值可能来自 JSON 解析并将其转换为 AudioClip。您可能可以在不同的线程上执行此操作:

Task<byte[]> ParseJsonData (string rawJson)
{
try
{
return Task.Run(() =>
{
jsonData = JSON.Parse(rawJson);
string stringData = jsonData["audioContent"]["data"].ToString();
return AudioHelpers.ConvertToByteStream(stringData);
});
}
catch (Exception e)
{
UnityEngine.Debug.LogException(e);
throw;
}
}

然后这样调用它:

IEnumerator getResponse(Conversation conversation)
{
WWWForm form = new WWWForm();
form.AddField("id", this.id);
var www = UnityWebRequest.Post("http://" + Datastore.Instance.host + ":3000/generate", form);

yield return www.SendWebRequest();

if (interrupted) yield break;

if (www.isNetworkError)
{
Debug.Log(www.error);
}
else
{
if (www.GetResponseHeaders().Count > 0)
{
ParseJsonData(rawJson)
.ContinueWith(ParseAudioData);
ParseAudioData(www.downloadHandler.text).ContinueWith((rawData) =>
{
// https://github.com/PimDeWitte/UnityMainThreadDispatcher
UnityMainThreadDispatcher.Instance.Enqueue(() =>
{
AudioClip clip = AudioHelpers.ConvertToAudioClip(rawData);
StartCoroutine(PlayDialog(clip));
Debug.Log("Response Recieved");
});
}
}
}

IEnumerator PlayDialog (AudioClip clip)
{
this.audioSource.clip = clip;
this.audioSource.Play();
this.animator.SetBool(this.talkingBoolHash, true);
yield return new WaitForSeconds(clip.length);
if (interrupted) yield break;
this.conversation.currentSpeaker = jsonData["nextSpeaker"].ToString().Replace("\"", "");
this.conversation.processing = false;
this.animator.SetBool(this.talkingBoolHash, false);
}

这会将操作发送到新线程,并在操作结束时继续执行您的代码。如果滞后峰值来自解析 json,这应该会有所帮助。从其他线程运行代码时要小心,因为您无法访问大部分 Unity 功能,而且它们都是异步的。

编辑:包括一些用于在 MainThread 上运行代码的实用程序,因为正如其他人所指出的,如果不摆弄同步上下文,这将无法工作。

此外,我建议您尝试将您的方法与单一职责分开。截至目前,您的协程正在下载内容、解析内容、播放音频和更新对话状态。这对于单个函数来说已经相当多了。

你的 JSON 也应该是一个结构化的类,解析它并仍然通过它的字符串哈希访问东西是没有意义的。

如果这仍然不能解决您的问题,您可能需要更深入地研究探查器并检查到底是什么导致它在主线程中花费太多时间。

关于c# - 主线程上的网络请求导致帧率延迟峰值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68796361/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com