Najbardziej wydajny sposób konwersji Vector3 na Vector2

11

Jaki jest najbardziej wydajny i najszybszy sposób na konwersję Vector3 na Vector2?

Odlew:

Vector2 vector2 = (Vector2)vector3;

Inicjowanie nowego Vector2:

Vector2 vector2 = new Vector2(vector3.x, vector3.y);

Czy jest jeszcze inna metoda, której nie znam?

S. Tarık Çetin
źródło
11
Tego rodzaju operacje strukturalne nigdy nie będą wąskim gardłem determinującym wydajność w twojej grze, więc zamiast zagłębiać się w takie mikrooptymalizacje, polecam użycie tego, co jest bardziej zrozumiałe w kontekście, którego używasz to. ;)
DMGregory
3
@DMGregory: O ile, oczywiście, OP nie wykonał już analizy wydajności i, być może z powodu boksu, faktycznie ma to w zagnieżdżonej pętli powodującej problem z wydajnością. Taka zagnieżdżona pętla może być na przykład implementacją gwiazdy A lub Dijkstry.
Pieter Geerkens,
5
@PieterGeerkens Fair, ale gdyby OP przeprowadzał już analizę wydajności, wypróbowaliby już obie metody i mieliby liczby na obu. ;) Patrząc na trajektorię wielu nowych użytkowników Unity (w tym siebie), jestem całkiem pewien, że w tym przypadku jest to mikrooptymalizacja, więc chciałem ostrzec przed nią (jeśli być może zawyżone). W ten sposób leżą tygodnie lub miesiące poprawiania kodu i martwienia się o optymalność w sposób, który nie poprawia naszych gier.
DMGregory

Odpowiedzi:

12
Vector3 v3 = Vector3.one;
Vector2 v2 = v3;

Vector3s można niejawnie przekonwertować na Vector2 (z jest odrzucane).

http://docs.unity3d.com/ScriptReference/Vector2-operator_Vector3.html

Jeśli musisz dokonać wielu konwersji, być może będziesz musiał zmienić sposób korzystania z wektorów. Wykonaj dwa testy i określ czas, który z nich będzie dla Ciebie odpowiedni.

AKTUALIZACJA Z TESTAMI: Ponieważ zapytałeś, który jest najszybszy , stworzyłem test z 10000000 konwersji każdego w Unity. Wygląda na to, że w tym przypadku najszybsza jest wersja inicjująca. ALE powinieneś zawsze używać tego, który pasuje do twojego kontekstu, więc radzę ci przeprowadzić własne testy w grze.

TestConvertByOperation 10000000 wystąpień: 0,2714049s

TestConvertByCasting 10000000 instancji: 0,286027s

TestConvertByInitializing 10000000 instancji: 0,1458781s

using UnityEngine;

public class TestVector3Conversion : MonoBehaviour
{

    readonly int iterations = 10000000;
    Vector3 testVector = new Vector3(3f, 14f, 42f);

    void Start()
    {
        Debug.Log(string.Format("TestConvertByOperation {0} instances: {1}s", iterations, TestConvertByOperation()));
        Debug.Log(string.Format("TestConvertByCasting {0} instances: {1}s", iterations, TestConvertByCasting()));
        Debug.Log(string.Format("TestConvertByInitializing {0} instances: {1}s", iterations, TestConvertByInitializing()));
    }

    float TestConvertByOperation()
    {
        var timeStart = Time.realtimeSinceStartup;

        for (int i = 0; i < iterations; i++)
        {
            Vector2 v2 = testVector;
        }

        return Time.realtimeSinceStartup - timeStart;
    }

    float TestConvertByCasting()
    {
        var timeStart = Time.realtimeSinceStartup;

        for (int i = 0; i < iterations; i++)
        {
            Vector2 v2 = (Vector2)testVector;
        }

        return Time.realtimeSinceStartup - timeStart;
    }

    float TestConvertByInitializing()
    {
        var timeStart = Time.realtimeSinceStartup;

        for (int i = 0; i < iterations; i++)
        {
            Vector2 v2 = new Vector2(testVector.x, testVector.y);
        }

        return Time.realtimeSinceStartup - timeStart;
    }

}
Mattias
źródło
1
Są niejawnie obsadzone. Odbywa się to poprzez zdefiniowanie nowych operatorów konwersji . Jak na ironię Unity narusza „... jeśli konwersja gwarantuje, że nie spowoduje utraty danych”. część.
Athos vk
1
Zaktualizowałem moją odpowiedź o przykładowy kod, aby przetestować różne podejścia. Daj mi znać, który jest szybszy w twoim przypadku.
Mattias
1
Wyniki zmieniają się nieznacznie w kompilacji wydania / bez debugowania lub gdy dane Vector2 mają cykl życia poza pętlą for (co uniemożliwia kompilatorowi wykonywanie pewnych rodzajów optymalizacji). Dostaję spread od 110 do 151 milisekund lub maksymalną różnicę około 4 nanosekund na zadanie. Więc jeśli nie robimy tego setki tysięcy razy w każdej klatce, prawdopodobnie nie jest to powód do zmartwień, nawet jeśli istnieje wymierna różnica w syntetycznym przykładzie takim jak ten.
DMGregory
1
@DMGregory uzgodnione. Dlatego zawsze dobrym pomysłem jest przeprowadzanie testów wydajności we właściwym kontekście z rzeczywistymi danymi.
Mattias
1
Problem z niejawną konwersją jest taki, że ysię skończył. Konwertując Vector3na a Vector2, prawie zawsze chcesz, xa znie xi y.
Kevin Krumwiede,
6

Zarówno Vector2, jak i Vector3strukturą w silniku Unity, więc tworzenie jednego z drugiego wymaga po prostu alokacji pamięci na stosie (chyba że miejsce docelowe jest atrybutem obiektu klasy , co pozwoliłoby pominąć ten pierwszy krok) i kopiowanie dwóch wartości składników. Oba podane mechanizmy powinny zostać skompilowane z dokładnie tym kodem IL.

Jeśli napotykasz problem z wydajnością podczas konwersji tego typu, prawdopodobnie masz problem z boksem , przy czym struktura jest konwertowana na obiekt klasy, a następnie z niego . W takim przypadku powinieneś sprawdzić, czy, kiedy i jak można uniknąć boksu w częściach kodu o kluczowym znaczeniu dla wydajności.

Pieter Geerkens
źródło
0

Z mojego testu najlepszym sposobem na to jest samodzielne przypisanie jego wartości.

Vector2 vector2;
vector2.x = vector3.x;
vector2.y = vector3.y;

To mój wynik, który przedłużam od Mattiasa.

TestConvertByOperation 10000000 instancji: 0.3220527s

TestConvertByCasting 10000000 instancji: 0,3226218s

TestConvertByInitializing 10000000 instancji: 0,1916729s

TestConvertByManualAssign 10000000 instancji: 0,09500527s

using UnityEngine;

namespace myTest
{
    public class test: MonoBehaviour 
    {
        readonly int iterations = 10000000;
        Vector3 testVector = new Vector3(3f, 14f, 42f);

        void Start()
        {
            Debug.Log(string.Format("TestConvertByOperation {0} instances: {1}s", iterations, TestConvertByOperation()));
            Debug.Log(string.Format("TestConvertByCasting {0} instances: {1}s", iterations, TestConvertByCasting()));
            Debug.Log(string.Format("TestConvertByInitializing {0} instances: {1}s", iterations, TestConvertByInitializing()));
            Debug.Log(string.Format("TestConvertByManualAssign {0} instances: {1}s", iterations, TestConvertByManualAssign()));
        }

        float TestConvertByOperation()
        {
            var timeStart = Time.realtimeSinceStartup;
            Vector2 v2;
            for (int i = 0; i < iterations; i++)
            {
                v2 = testVector;
            }

            return Time.realtimeSinceStartup - timeStart;
        }

        float TestConvertByCasting()
        {
            var timeStart = Time.realtimeSinceStartup;
            Vector2 v2;
            for (int i = 0; i < iterations; i++)
            {
                v2 = (Vector2)testVector;
            }

            return Time.realtimeSinceStartup - timeStart;
        }

        float TestConvertByInitializing()
        {
            var timeStart = Time.realtimeSinceStartup;
            Vector2 v2;
            for (int i = 0; i < iterations; i++)
            {
                v2 = new Vector2(testVector.x, testVector.y);
            }

            return Time.realtimeSinceStartup - timeStart;
        }
        float TestConvertByManualAssign()
        {
            var timeStart = Time.realtimeSinceStartup;
            Vector2 v2;
            for (int i = 0; i < iterations; i++)
            {
                v2.x = testVector.x;
                v2.y = testVector.y;
            }

            return Time.realtimeSinceStartup - timeStart;
        }
    }
}

Uwaga: testuję to w wersji jedności 5.6.5

Vimutti Roatkanjanaporn
źródło