Obecnie próbuję ulepszyć kilka modułów pod względem wydajności.
Niektórzy z was mogą znać użycie walk()
metody zbierania, która jest bardzo przydatna, aby uniknąć bezpośredniego przechodzenia między produktami.
Ponadto dzięki @Vinai można również użyć delete()
metody zbierania .
Zauważyłem jednak, że rodzime pliki Magento 1 nie zawsze używają żadnej z tych metod usuwania.
Jednym z najgorszych kodów, jakie widziałem, jest massDelete()
metoda, z app/code/core/Mage/Adminhtml/controllers/Catalog/ProductController.php
której produkty są ładowane w pętli przed usunięciem .
foreach ($productIds as $productId) {
$product = Mage::getSingleton('catalog/product')->load($productId);
Mage::dispatchEvent('catalog_controller_product_delete', array('product' => $product));
$product->delete();
}
Przeprowadziłem więc testy wydajności, dodałem kilka wywołań logowania, aby sprawdzić czas i zużycie pamięci na usunięcie 100 produktów.
Test 1: walk
metoda
Zastąpiłem oryginalny kod wklejony powyżej tym kodem:
$collection = Mage::getResourceModel('catalog/product_collection')
->addAttributeToSelect('entity_id')
->addIdFilter($productIds)
->walk('delete');
A moje wyniki są następujące na moim gównianym serwerze deweloperskim (średnia z 10 testów):
- Kod oryginalny: 19,97 sekundy, użyto 15,84 MB
- Kod niestandardowy: 17,12 sekund, użyto 15,45 MB
Tak więc przy usuwaniu 100 produktów mój niestandardowy kod jest 3 sekundy szybszy i zużywa 0,4 MB mniej.
Test 2: przy użyciu delete()
metody zbierania
Zamieniłem oryginalny kod na ten:
$collection = Mage::getResourceModel('catalog/product_collection')
->addAttributeToSelect('entity_id')
->addIdFilter($productIds)
->delete();
A umysł jest zdumiony :
- Kod oryginalny: 19,97 sekundy, użyto 15,84 MB
- Kod niestandardowy: 1,24 sekundy, użyto 6,34 MB
Tak więc dla usunięcia 100 produktów mój niestandardowy kod jest 18 sekund szybszy i zużywa 9 MB mniej.
Jak stwierdzono w komentarzach, wygląda na to, że ta metoda nie wyzwala zdarzeń Magento (po załadowaniu, po usunięciu) ani opróżnieniu indeksu / pamięci podręcznej.
Pytanie
Moje pytanie brzmi zatem: czy istnieje powód, dla którego główny zespół Magento nie zastosował metody walk('delete')
zbierania lub zdarzenia, delete()
zamiast ładować produkty w pętli (co, jak wszyscy wiemy, jest bardzo złą praktyką)?
Głównym celem jest świadomość takich kluczowych punktów w przypadku rozwoju modułu: czy są jakieś szczególne przypadki, w których nie można użyć metody walk
/ collection delete()
?
EDYCJA: powód zdecydowanie nie jest spowodowany catalog_controller_product_delete
wysłaniem zdarzenia, ponieważ ten sam kod można znaleźć w kilku miejscach (sprawdź massDelete
metody) w rdzeniu Magento. Użyłem przykładu produktów do podkreślenia wydajności, ponieważ zwykle są to największe podmioty
źródło
getSingleton()
jako miary wydajności, zamiast oczywistego użycia kolekcji. Aha, możliwe jest również wywołanie zdarzenia za pomocą kolekcji, ale nie za pomocąwalk()
skrótu.delete()
wykonuje zapytanie DELETE zamiast ładowania kolekcji i usuwania każdego produktu. Dzięki temu naprawdę przegrasz wydarzenia.Odpowiedzi:
Uwaga dodatkowa, ale należy do tego skorzystać z programu Varien Profiler!
Chociaż nie mam wątpliwości, że Twoja zmiana poprawiłaby wydajność, przydatne byłoby podanie wyników „przed” w celu porównania ulepszeń.
Z innych pytań na tym forum wiemy, co następuje:
Sugerowałbym więc, że przykład, który znalazłeś, jest prawdopodobnie jednym z potencjalnie wielu klejnotów ukrytych w kodzie, które zostały napisane dawno temu i / lub przez mniej doświadczonego programistę. Podobnie jak większość kodu podstawowego (i kodu społeczności!) Zostałby przetestowany na małym zestawie danych, a nie przetestowany w walce, więc wydajność może nie być ściśle monitorowana.
Czy Twoje ulepszenie jest korzystne i czy jest bardziej dostosowane do najlepszych praktyk niż oryginalny kod? Tak. Jako programista społeczności Magento [1.x] nie masz jednak możliwości wprowadzania sugerowanych ulepszeń, tak jak w przypadku Magento 2, więc sugeruję, aby zaimplementować to w module lokalnym, jeśli wymaga tego wydajność w jednym ze sklepów lub zignoruj go, jeśli nie wpływa to na ciebie, ale zauważyłeś to podczas badań.
Jako aktualizacja edycji pytania, jestem pewien, że wiesz, że metoda walk w Varien_Data_Collection akceptuje dowolne wywołanie zwrotne, więc możesz użyć go do wszystkiego, co chcesz. Aby wywołać zdarzenie w oryginalnym przykładzie, możesz to zrobić za pomocą funkcji marszu, a także usunięcia.
Jedynym powodem, dla którego mogłem sobie wyobrazić, że załadowanie produktu przed jego usunięciem byłoby przydatne, może być to, że obserwatorzy dołączeni do tego wydarzenia mogą potrzebować pełnego zestawu danych niedostępnego bez uprzedniego załadowania produktu. W takim przypadku wyjaśniłoby to, dlaczego używają singletonu, a nie modelu, aby przynajmniej zminimalizować koszty ogólne obiektu.
źródło
Myślę, że robią to, aby odpalić
catalog_controller_product_delete
zdarzenie, którego używa Mage_Tag.catalog_product_delete_before
lubcatalog_product_delete_after
oznaczałoby to, że nie było to konieczne, choć myślałem. Zastanawiam się, czy to konkretne zdarzenie jest również używane do rejestrowania działań administratora.źródło
massDelete()
działaniaCustomerController.php
Myślę, że masowe usuwanie powinno działać jak usuwanie pojedynczego (w pełni załadowanego) produktu.
Ponieważ
$collection->delete()
odpowiedź jest już podana. Jeśli nie uruchomiszdeleter_before
,delete_after
prawdopodobnie mógłbym złamać niektóre rozszerzenia i ominąć niektórych obserwatorów używanych w jądrze.$collection->walk('delete')
prawdopodobnie działałoby, ale nadal ma tę wadę, że dane produktu nie są kompletne. Może to również uszkodzić niestandardowych obserwatorów, jeśli opierają się na dodatkowych danych, na przykład na obiekcie przedmiotu magazynowego.Chyba, jeśli zmieni
->addAttributeToSelect('entity_id')
się->addAttributeToSelect('*')
i dodaj->setFlag('require_stock_items', true)
(aby dodać dane do produktów seryjnych) nie wykona lepiej niż „pętla-Delete”.Wygląda na zły styl, ale myślę, że jest odpowiedni zarówno do akcji masowego usuwania.
Używam
walk()
idelete()
do niestandardowych modeli, ale wiem, że nie ma obserwatorów lubentity_id
jest wystarczający. Wystarczy wspomnieć,walk()
że działałby ze wszystkimi zdarzeniami używanymi w rdzeniu, ponieważ one wykorzystują tylko$product->getId()
, ale nie wiesz o obserwatorach zewnętrznych.źródło