Solange Du nicht Werte (Parameter) zur Verarbeitung übergeben willst ist da im eigentlich kein Unterschied.

Die zu wählende Variante hängt davon ab, ob in dem Unterprogramm Variablen (Informationen) aus dem Hauptgramm zu verarbeiten sind und ob es ein (oder mehrere) Rückgabewerte an das Hauptprogramm gibt.

Ein einfaches Beispiel:

Long1 = Val(String2)

Die Funktion Val konvertiert eine Nummer in einem Text in eine Nummerische Variable (z.B. Long). Mit dieser nummerischen Variable kann dann gerechnet werden. Wäre dieser Programmteil Val über ein GOSUB anzusprechen, dann müsste vorher der umzustellende String in eine Stringvariable kopiert werden, welche im Val Programmteil verwendet wird, da der Programmteil ja nur eine fixe globale Variable behandeln kann. Diese macht dann die Umwandlung dann in eine andere fixierte globale nummerische Variable (z.B. LONG). Nach Beendigung des Val Unterprogrammteiles müsste dann das Hauptprogramm den Wert wieder aus der num. Variablen herauskopieren, falls er noch länger gebraucht wird. Für jede Funktionaltät müsste SRAM reserviert werden.
Diese Variablen, mit welchen der Programmteil Val arbeitet, wären nur von diesem verwendbar und die String-Variable müsste dann auch auf den grössten vorkommenden String ausgelegt sein.

Wird aber eine FUNCTION oder SUB (wenn es keinen Rückgabewert gibt) verwendet, werden nur Zeiger auf die Variablen übergeben (bei byVal wird der Wert in einen temporären Speicherbereich Frame kopiert). Diese Zeiger verweisen auf die eigentlichen Variablen.
Der Code ist dann flexibler zu verwenden und braucht im allgemeinen weniger SRAM.

Ich hoffe, ich konnte Dir den Unterschied an diesem zugegebenermaßen etwas drastisch dargestellten Beispiel (worst case) klar machen.
Es hängt also immer davon ab, welche Informationen hat das Unterprogramm zu verarbeiten und was liefert es an das Hauptprogramm zurück.