wczoraj z okazji prac nad pewnym projektem i odwiecznego pytania: Doctrine vs Propel poszukałem i znalazłem
dla potomnych: http://code.google.com/p/php-orm-benchmark/source/browse/#svn/trunk/doctrine_12
prosty benchmark: PDO / Propel 1.4 / Propel 1.5 / Propel 1.5 ( with Cache ) / Doctrine 1.2 / Doctrine 2 / Doctrine 2 ( with Cache )
zanim zdecydujecie się samemu pogrzebać/testować pamiętajcie, że Doctrine 2 pracuje z php 5.3.2
oki.. wynik moich testów 'delikatnie' mnie zdziwił:
| Insert | findPk|complex| hydrate| with | |--------|--------|--------|--------|--------| PDOTestSuite | 153 | 154 | 110 | 116 | 103 | Propel14TestSuite | 1214 | 529 | 137 | 321 | 315 | Propel15TestSuite | 1122 | 725 | 250 | 426 | 439 | Propel15WithCacheTestSuite | 1026 | 453 | 171 | 346 | 331 | Doctrine12TestSuite | 2168 | 3697 | 569 | 1990 | 2545 | Doctrine2TestSuite | 189 | 503 | 426 | 1408 | 1284 | Doctrine2WithCacheTestSuite | 203 | 543 | 137 | 769 | 431 |
A może byś tak jeszcze napisał, co oznaczają te cyferki, jak uruchamiałeś, ogólnie metodologię? Takie pseudobenchmarki z cyferkami i bez słowa komentarza to o kant czterech liter można rozbić.
Natomiast Doctrine 1.x demonem szybkości nie jest i to nie jest żadna tajemnica, zwłaszcza jak robisz hydrację wyników do obiektów zamiast do tablic.
Zyx: specjalnie, żeby nie było pytań o metodologię i na użytek innych dałem link do skryptu, którym testowałem.
Jest to czas w mikrosekundach jaki dany ORM potrzebował na wykonanie zapytania.
Tu masz główną klasę abstract:
public function initTables() { try { $this->con->exec('DROP TABLE [book]'); $this->con->exec('DROP TABLE [author]'); } catch (PDOException $e) { // do nothing - the tables probably don't exist yet } $this->con->exec('CREATE TABLE [book] ( [id] INTEGER NOT NULL PRIMARY KEY, [title] VARCHAR(255) NOT NULL, [isbn] VARCHAR(24) NOT NULL, [price] FLOAT, [author_id] INTEGER )'); $this->con->exec('CREATE TABLE [author] ( [id] INTEGER NOT NULL PRIMARY KEY, [first_name] VARCHAR(128) NOT NULL, [last_name] VARCHAR(128) NOT NULL, [email] VARCHAR(128) )'); } public function run() { $t1 = $this->runTest('runAuthorInsertion', 1700); $t1 += $this->runTest('runBookInsertion', 1700); $t2 = $this->runTest('runPKSearch', 1900); $t3 = $this->runTest('runComplexQuery', 190); $t4 = $this->runTest('runHydrate', 750); $t5 = $this->runTest('runJoinSearch', 700); http://www.php.net/echo http://www.php.net/sprintf("%30s | %6d | %6d | %6d | %6d | %6d |\n", get_class($this), $t1, $t2, $t3, $t4, $t5); }
function runAuthorInsertion($i) { $author = new Author(); $author->first_name = 'John' . $i; $author->last_name = 'Doe' . $i; $author->save($this->con); $this->authors[]= $author->id; } function runBookInsertion($i) { $book = new Book(); $book->title = 'Hello' . $i; $book->author_id = $this->authors[http://www.php.net/array_rand($this->authors)]; $book->isbn = '1234'; $book->price = $i; $book->save($this->con); $this->books[]= $book->id; } function runPKSearch($i) { $author = Doctrine_Core::getTable('Author')->find($this->authors[http://www.php.net/array_rand($this->authors)]); } function runComplexQuery($i) { $authors = Doctrine_Query::create() ->from('Author a') ->where('a.id > ?', $this->authors[http://www.php.net/array_rand($this->authors)]) ->orWhere('(a.first_name || a.last_name) = ?', 'John Doe') ->limit(5) ->count(); } function runHydrate($i) { $books = Doctrine_Query::create() ->from('Book b') ->where('b.price > ?', $i) ->limit(5) ->execute(); foreach ($books as $book) { // removing the record from the instance pool, otherwise we are not testing hydration time! $book->free(true); } } function runJoinSearch($i) { $book = Doctrine_Query::create() ->from('Book b') ->leftJoin('b.Author a') ->where('b.title = ?', 'Hello' . $i) ->limit(1) ->fetchOne(); }
Widzę, że dałeś. Sęk w tym, że nie wszyscy mają czas, by to ściągać, uruchamiać i rozkminiać wszystko metodą inżynierii wstecznej tylko po to, by się dowiedzieć, że wyniki są w mikrosekundach, bo autor benchmarka umarłby z wysiłku, gdyby o tym wspomniał. Ponadto sam kod nie mówi nic o tym czy np. testy uruchamiałeś kilkakrotnie, czy i jak uśredniałeś wyniki, czyli o wszystkich tych rzeczach związanych z późniejszą obróbką.
Powtarzam:
Zyx: testy były uruchamiane min 10 razy na 3 różnych maszynach, wynik ciągle przybliżony. A co do Doctrine 1.2 to jest 'załamany' jego wydajnością. I poważnie zastanawiam się co dalej.. Wybiorę Propel to niedługo ( albo i już ) będzie dodatkowym pluginem do Symfony. Głównym jest doctrine 1.2 a potem ma być doctrine 2. Z tego co widzę to coraz więcej pluginów jest robionych pod Doctrine ;/ ciężki wybór
W porządku, to może jeszcze byś odpowiedział na wątpliwość, którą już trzeci raz powtórzę:
Zyx: wiem, że hydrację wyników do obiektów zamiast do tablic jest wolniejsza, dziś zrobie te same testy z hydracją do tablic przy insert i szukaniu po PK.
Jeżeli możesz podziel się linkiem do dokumenacji gdzie jest mowa o: "skoro sami autorzy piszą w dokumentacji, że jest ona dość wolna i aby nie używać jej".
oki chyba tak .. bo tylko to znalazłem na stronie Doctrine. Dziś nowe testy ;] zobaczymy jak wyjdzie.
Wracam do zapomnianego wątku
w przykładach było wyraźnie zaznaczone, że nie liczony jest czas hydracji :
foreach ($books as $book) { // removing the record from the instance pool, otherwise we are not testing hydration time! $book->free(true); }
foreach ($books as $book) { // removing the record from the instance pool, otherwise we are not testing hydration time! $book->free(true); }
$query = new Doctrine_Query(); $query->from('Product p'); $query->leftJoin("p.Translation t1 INDEXBY t1.lang"); $query->leftJoin("p.accessories a INDEXBY a.id"); $query->leftJoin("a.Translation t2 INDEXBY t2.lang"); $query->leftJoin("p.countries c INDEXBY c.id"); $query->leftJoin("c.Translation t3 INDEXBY t3.lang"); $query->leftJoin("p.product_category pc"); $query->leftJoin("pc.Translation t4 INDEXBY t4.lang"); $query->addOrderBy('t4.name'); $query->addOrderBy('p.position'); $query->addOrderBy('t1.name'); //$query->setHydrationMode(Doctrine::HYDRATE_ARRAY); // 2x szybsze $query->setHydrationMode(Doctrine::HYDRATE_RECORD); $query->execute();
słaby to jest Doctrine i myślenie niektórych o nowościach promowanych przez Fabiena:
kawałek kodu którego użyłem to pomiary z hydracją:
function runComplexQuery($i) { $authors = Doctrine_Query::create() ->from('Author a') ->where('a.id > ?', $this->authors[http://www.php.net/array_rand($this->authors)]) ->orWhere('(a.first_name || a.last_name) = ?', 'John Doe') ->limit(5) ->setHydrationMode(Doctrine::HYDRATE_RECORD) ->execute(); } function runHydrate($i) { $books = Doctrine_Query::create() ->from('Book b') ->where('b.price > ?', $i) ->limit(5) ->setHydrationMode(Doctrine::HYDRATE_RECORD) ->execute(); /*foreach ($books as $book) { // removing the record from the instance pool, otherwise we are not testing hydration time! $book->free(true); }*/ }
function runComplexQuery($i) { $authors = Doctrine_Query::create() ->from('Author a') ->where('a.id > ?', $this->authors[http://www.php.net/array_rand($this->authors)]) ->orWhere('(a.first_name || a.last_name) = ?', 'John Doe') ->limit(5) ->setHydrationMode(Doctrine::HYDRATE_RECORD) ->execute(); } function runHydrate($i) { $books = Doctrine_Query::create() ->from('Book b') ->where('b.price > ?', $i) ->limit(5) ->setHydrationMode(Doctrine::HYDRATE_RECORD) ->execute(); /*foreach ($books as $book) { // removing the record from the instance pool, otherwise we are not testing hydration time! $book->free(true); }*/ }
Wyniki powiem, że mnie bardzo zadziwiły - myślałem, że PDO będzie na samym końcu, a tutaj niespodzianka, najszybszy, konkurencja bez szans - no ale jednak, co stworzone przez twórców języka, chodzi troszkę lepiej. ;p
@fifi209: Może dlatego, że PDO nie jest ORMem?
PDO pewnie ma "łatkę" wolnego bo gdyby puścić benchmark najprostszego api (mysql_*) to czasy byłyby prawdopodobnie jeszcze lepsze. Kiepskie wyniki ORM-ów wcale mnie nie dziwią. PHP do najszybszych języków nie należy, kod wypluwany przez ORM-y też z wydajnością ma niewiele wspólnego.
Z resztą żadna warstwa abstrakcji ani wirtualizacji jeszcze nigdy niczego nie przyspieszyła. Założenia przy tworzeniu takich systemów to nie wzrost prędkości.
Oczywiście macie rację, że czas programisty kosztuje dużo więcej niż nawet najlepszy sprzęt i gdyby kod był nawet 10 razy wolniejszy ale projekt można byłoby skończyć tydzień szybciej to by się pewnie w wielu przypadkach opłaciło. Jednak problem jest inny. SQL to świetny model, OOP do którego tłumaczy ORM jest strasznie "drewniane". Może wam się wydaje, że zaoszczędzicie trochę czasu na pisaniu prostych selectów po kluczu, ale szybko zrozumiecie, że kiedy trzeba zrobić coś bardziej skomplikowanego to SQL jest nie dość, że dużo prostsze to jeszcze bardzo elastyczne i łatwe w modyfikacji.
Lepiej bardzo dobrze poznać SQL niż uczyć się redundantnej technologii której jedynym plusem jest to, że chroni osoby całkowicie zielone przed SQL-Injection. Ściągnąć sobie jakieś dobre narzędzie do graficznego projektowania baz, żeby nie tracić czasu na klepanie w kółko tego samego. Zwłaszcza, że jeśli zaczniecie jakieś bardziej skomplikowane projekty to i tak się skończy na analizie czystego SQL-a i pisaniu kwerend z palca (macie 5 krotny spadek wydajności dla prostych selectów, dla bardziej skomplikowanych analiz danych zależnie od optymalizacji kwerendy mogą się wykonywać 2 dni albo 2 minuty).
Dla tabel poniżej 10-100k wierszy optymalizacja nie ma znaczenia, dlatego wydaje się, że ORM jest taki fajny... nawet nie taki wolny i tak się w nim szybko pisze projekty. Projekt się rozrośnie, będziecie brali ten garbage który wypluwa wasz ulubiuony ORM, wrzucali przed to EXPLAIN i klęli
http://en.wikipedia.org/wiki/Technical_debt
Design debt... szybciej stworzycie pierwszą wersję skryptu, ale za złą decyzję projektową jaką jest w większości wypadków użycia ORMa (jakiegokolwiek) będziecie "płacić" wolnym rozwojem projektu przez cały cykl życia kodu.
Czy może ktoś porównywał najnowsze wersje Propela i Doctrine - czyli dwójki? Jestem na etapie decyzji, który ORM wybrać - Doctrine2 czy Propel 2, ale dosłownie nigdzie w sieci nie ma żadnych testów i ocen. Są tylko wpisy na temat starych wersji. Znalazłem artykuł http://www.vertabelo.com/blog/technical-articles/side-by-side-doctrine2-and-propel-2-comparison, ale on zawiera porównanie cech i funkcjonalności. Wiem, że to też jest bardzo ważne, ale chciałbym, żeby ktoś mi wyraźnie napisał, którego ORM użyć (czyli który ORM jest lepszy jego zdaniem) i dlaczego. Ktoś pomoże?
Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)