Canvas render make lags

I have written a system of dialogues with game characters. It works simply: a player approaches another player, looks at him and presses the E key. A ray is thrown, it receives a RaycastHit and runs a dialog script from it. At this stage, the DialogueBox user interface is enabled, which contains all the main components for displaying the dialog on the screen. For some reason, there is a delay when pressing the E key and the dialog box appears. There are no delays at the further beginning of the dialogue with this character.

I looked at Profiler and found that the roots of the problem come from the Canvas render. How can I fix this problem, is there a way to get rid of lags if, say, I render the Canvas element in advance?

I also realized that the introduction of dialog texts into the queue is implemented through foreach, which can slow down the program

Script:

using System.Collections;
using System.Collections.Generic;
using TMPro;
using UnityEngine;

public class DialogueSystem : MonoBehaviour
{
    public bool isActive;

    [SerializeField] private GameObject dialogueBox;
    [SerializeField] private PlayerMovement playerMovement;
    [SerializeField] private PlayerCamera playerCamera;

    [SerializeField] private TextMeshProUGUI speakerNameText;
    [SerializeField] private TextMeshProUGUI dialogueText;

    private Queue<string> _sentences;

    private void Start()
    {
        _sentences = new Queue<string>();
        dialogueBox.SetActive(false);
    }

    public void StartDialogue(Dialogue dialogue)
    {
        dialogueBox.SetActive(true);
        speakerNameText.text = dialogue.speakerName;

        SetCursorState(CursorLockMode.None, true);
        playerMovement.enabled = false;
        playerCamera.enabled = false;

        _sentences.Clear();

        foreach (string sentence in dialogue.sentences)
        {
            _sentences.Enqueue(sentence);
        }

        isActive = true;

        DisplayNextSentence();
    }

    public void DisplayNextSentence()
    {
        if (_sentences.Count == 0)
        {
            EndDialogue();
            return;
        }

        string sentence = _sentences.Dequeue();
        StopAllCoroutines();
        StartCoroutine(TypeSentence(sentence));
    }

    IEnumerator TypeSentence(string sentence)
    {
        dialogueText.text = "";
        foreach (char letter in sentence)
        {
            dialogueText.text += letter;
            yield return null;
        }
    }

    void EndDialogue()
    {
        SetCursorState(CursorLockMode.Locked, false);
        playerMovement.enabled = true;
        playerCamera.enabled = true;
        dialogueBox.SetActive(false);

        isActive = false;
    }

    void SetCursorState(CursorLockMode mode, bool visible)
    {
        Cursor.lockState = mode;
        Cursor.visible = visible;
    }
}

Notes on optimizing UnityEngine.UI setups:

In general, you want to separate your UI elements into groups that are dynamic or static and use different canvases for each of them. In some cases you may even want to further subdivide into more canvases based on how frequently they update (every frame, every second, once per interaction from user, etc…).

If you want more details on why, here are some very old talks about performance optimizations. So of the info may be quite outdated but I believe not much has likely changed with UGUI since they’ve basically stopped working on it. I time-stamped the parts relevant to UGUI.