Time inefficiencies » Historie » Revize 6
« Předchozí |
Revize 6/8
(rozdíl)
| Další »
Jan Krajňák, 2023-06-05 21:22
Time inefficiencies¶
ARAP¶
1) V konstruktoru třídy arap by výpočet m_solver.compute(m_transpose * m_transpose.transpose()) mohl být časově náročný, pokud je matice velká. Tento výpočet se provádí při každém vytvoření instance třídy arap, ale nezávisí na nějakých vstupních parametrech. Pokud je matice m_transpose pro všechny instance konstantní, lze tento výpočet přesunout mimo konstruktor a provést jej pouze jednou.
2) V metodě arap::difference_matrix smyčka for (auto edge : *edges) iteruje přes všechny hrany, ale používá operaci ret.row(idx++) = this_pos - Eigen::Vector3d(get_position(edge.end));, která by mohla být časově velmi drahá, pokud je počet hran velký. Jedním z vylepšení by mohlo být předalokovat matici ret se správnou velikostí před smyčkou a pak přiřadit hodnoty přímo řádkům například pomocí indexování.
3) V metodách arap::dynamic_right_side a arap::fixed_variables iteruje smyčka for (auto edge : *edges) přes všechny hrany pro každý vrchol. Místo opakovaného načítání hran pro každý vrchol by mohlo být efektivnější načíst hrany jednou mimo smyčku a znovu je použít uvnitř smyčky.
PBD¶
1) V metodě pbd::init se pole scénářů a příloh používají k mapování indexů strategií scénářů a příloh na odpovídající ukazatele členských funkcí. Tato pole se však vytvářejí a inicializují při každém volání metody init. Pro zvýšení efektivity lze tato pole přesunout mimo metodu a deklarovat je jako statické nebo globální proměnné, aby se zabránilo zbytečnému vytváření a inicializaci.
2) Metoda pbd::get_graph jednoduše vrací členskou proměnnou m_graph. Pokud je metoda get_graph volána často, bylo by efektivnější přistupovat k členské proměnné přímo než vytvářet metodu.
3) V metodě pbd::execute je smyčka for (size_t a = m_start; a < m_end; a++), která iteruje přes svalové uzly. Uvnitř smyčky se síla působící na každý uzel nastaví pomocí double* f = m_graph->node(a).get\_force();. To zahrnuje načtení ukazatele síly pro každý uzel v grafu, což by mohlo být neefektivní -- pokud by načtení síly zahrnovalo složité výpočty nebo vzorce přístupu do paměti.
4) V metodě pbd::execute je výpočet proměnné grav pomocí double\ grav = std::pow(10, m_gravity) - 1;. Tento výpočet zahrnuje mocninnou funkci a odečítání. Pokud hodnota m_gravity zůstává během provádění metody execute konstantní -- předvýpočet této hodnoty mimo cyklus by mohl být časově efektivnější.
5) Ve funkci laplacianSmoothing -- Kód alokuje nové pole smoothed_pos pro každý vrchol ve svalovém modelu. Tato alokace se provádí uvnitř vnitřní smyčky, což může být nákladné z hlediska alokace a dealokalizace paměti. Efektivnější by bylo alokovat pole jednou před smyčkou a znovu ho použít.
6) Funkce applyLaplacianSmoothing je volána pro každý vrchol uvnitř vnější smyčky. Namísto použití vyhlazování pro každý vrchol zvlášť by bylo efektivnější shromáždit vyhlazené pozice pro všechny vrcholy a pak je aktualizovat společně mimo smyčku.
Ve třídě PBD je několik smyček, u kterých lze potenciálně využít paralelizaci pro zvýšení výkonu. Tyto smyčky zahrnují iterace nad vrcholy, kolizemi, modely svalů a trojúhelníky. Paralelizací těchto smyček lze provádět více iterací současně, využít výkon vícejádrových procesorů a potenciálně snížit celkovou dobu provádění. Použitím paralelizačních technik lze tyto smyčky provádět paralelně, čímž se potenciálně zvýší výkon a sníží celková doba výpočtu. Při paralelizaci kódu je však důležité zajistit správnou synchronizaci a mechanismy sdílení dat, aby se předešlo závodním podmínkám a zachovala se konzistence dat.
Graph¶
1) V konstruktoru třídy graf je alokována paměť pro různá pole (m_arr, m_collision, m_volume, m_polys, m_poly_neighbours, m_normals). Alokace paměti se však provádí pomocí operátoru new, což může být u velkých bloků paměti pomalé. Lepších výsledků by bylo možné za použití efektivnějších technik alokace paměti, jako je std::vector nebo memory pooling.
2) Výpočet objemu -- Metoda calc_volume vypočítá objem modelu iterací přes všechny trojúhelníky a provedením vektorových výpočtů. Implementaci by však bylo možné optimalizovat použitím efektivnějšího algoritmu pro výpočet objemu, například Shoelaceova vzorce nebo věty o divergenci.
3) Funkce generate_collision_detection kontroluje, zda je m_collision[part] null, a v případě potřeby alokuje paměť pomocí new. Použití inteligentního ukazatele (např. std::unique_ptr) pro automatickou správu alokace a dealokování paměti by mohlo být efektivnější. Navíc to může pomoci vyhnout se únikům paměti a zvýšit bezpečnost kódu.
4) K lepším výkonům by mohlo vést i použití vektorové funkce namísto obyčejných smyček: Funkce update_normal provádí výpočty nad jednotlivými prvky polí pomocí běžných for cyklů. K efektivnějšímu provádění těchto výpočtů je možné využít funkce z knihovny <algorithm> nebo <numeric>, například std::transform.
5) Kód používá pro ukládání dat pole a vektory. Nicméně použití vhodnějších datových struktur, například std::unordered_map, std::unordered_set nebo std::set by mohlo být časově efektivnější. Tyto datové struktury mohou zajistit rychlejší vyhledávání a eliminovat potřebu ručního vyhledávání nebo hashování.
6) Funkci log_of_int lze zjednodušit a optimalizovat. Místo smyčky a posunu doprava je možné k výpočtu binárního logaritmu použít funkci std::log2.
7) Ve funkci update_stiffness je funkce get_edges opakovaně volána ve smyčce pro každý uzel. Efektivnější by bylo zavolat ji jednou před smyčkou a výsledek uložit do lokální proměnné.
Aktualizováno uživatelem Jan Krajňák před téměř 2 roky(ů) · 6 revizí