В этой заметке я расскажу о своих результатах тестирования производительности двух ORM: xPDO и Propel. Весь код выложен на GitHub тут: https://github.com/andchir/xpdo_propel_comparison.
Я создал 3 таблички: книги, авторы и издательства. Сгенерировал 1000 книг в БД, плюс 100 авторов и 100 издательств. Не буду подробно останавливаться как создавал модели для табличек, это можно посмотреть на GitHub. По-моему для обеих ORM созданы абсолютно одинаковые условия.
Сначала самый простейший запрос, вытаскиваю по 100 записей из БД.
xPDO:
<?php use xPDO\xPDO; // setup the autoloading require_once __DIR__ . '/vendor/autoload.php'; require_once __DIR__ . '/config.php'; $startTime = microtime(true); $xpdo = xPDO::getInstance('aMySQLDatabase', [ xPDO::OPT_CACHE_PATH => __DIR__ . '/xpdo/cache/', xPDO::OPT_HYDRATE_FIELDS => true, xPDO::OPT_HYDRATE_RELATED_OBJECTS => true, xPDO::OPT_HYDRATE_ADHOC_FIELDS => true, xPDO::OPT_CONNECTIONS => [ [ 'dsn' => 'mysql:host=' . $config['db']['host'] . ';dbname=' . $config['db']['dbname'] . ';charset=utf8', 'username' => $config['db']['username'], 'password' => $config['db']['password'], 'options' => [ xPDO::OPT_CONN_MUTABLE => true, ], 'driverOptions' => [], ], ], ]); $xpdo->setLogLevel(xPDO::LOG_LEVEL_INFO); $xpdo->setLogTarget(XPDO_CLI_MODE ? 'ECHO' : 'HTML'); $limit = 100; $query = $xpdo->newQuery('bookstore\Book'); $query->sortby('title','ASC'); $query->limit($limit); $books = $xpdo->getCollection('bookstore\Book', $query); foreach($books as $book) { echo '<pre>' . print_r( $book->toJSON(), true ) . '</pre>'; } $endTime = microtime(true); echo '<br>Total records: ' . $limit; echo '<br>Total time: ' . sprintf('%f', ( $endTime - $startTime )); echo '<br>Memory: ' . round(memory_get_usage()/1024/1024, 4) . ' MB';
Propel:
<?php // setup the autoloading require_once __DIR__ . '/vendor/autoload.php'; $startTime = microtime(true); // setup Propel require_once __DIR__ . '/generated-conf/propel/config.php'; $limit = 100; $books = BookQuery::create() ->orderByTitle() ->limit($limit) ->find(); foreach($books as $book) { echo '<pre>' . print_r( $book->toJSON(), true ) . '</pre>'; } $endTime = microtime(true); echo '<br>Total records: ' . $limit; echo '<br>Total time: ' . sprintf('%f', ( $endTime - $startTime )); echo '<br>Memory: ' . round(memory_get_usage()/1024/1024, 4) . ' MB';
Результаты
xPDO:
Total records: 100 Total time: 0.073161 Memory: 0.9901 MB
Propel:
Total records: 100 Total time: 0.012900 Memory: 1.2067 MB
xPDO немного обошел по потреблению памяти, но Propel очень прилично выиграл по времени выполнения (в 5 с половиной раз быстрее).
Теперь усложним задачу. Делаем джоин одной таблички, вытаскиваем 500 записей.
xPDO:
<?php $limit = 500; $query = $xpdo->newQuery('bookstore\Book'); $query->sortby('title','ASC'); $query->limit($limit); $query->select($xpdo->getSelectColumns('bookstore\Book','Book')); $query->select(array('Author.first_name AS author_name', 'Author.last_name AS author_last_name')); $query->leftJoin('bookstore\Author', 'Author'); $books = $xpdo->getCollection('bookstore\Book', $query); foreach($books as $book) { echo '<pre>' . print_r( $book->toJSON(), true ) . '</pre>'; }
Propel:
<?php $limit = 500; $books = BookQuery::create() ->leftJoin('Book.Author') ->withColumn('Author.FirstName', 'AuthorFirstName') ->withColumn('Author.LastName', 'AuthorLastName') ->orderByTitle() ->limit($limit) ->find(); foreach($books as $book) { echo '<pre>' . print_r( $book->toJSON(), true ) . '</pre>'; }
Результаты
xPDO:
Total records: 500 Total time: 0.358788 Memory: 2.9128 MB
Propel:
Total records: 500 Total time: 0.051907 Memory: 2.1503 MB
Propel выполнил задачу почти в 7 раз быстрее чем xPDO, по памяти тоже немного обошел.
Теперь тоже будем вытаскивать данные со второй таблички, но сделаем это без джоина, а отдельным запросом (только для теста).
xPDO:
<?php $limit = 500; $query = $xpdo->newQuery('bookstore\Book'); $query->sortby('title','ASC'); $query->limit($limit); $books = $xpdo->getCollection('bookstore\Book', $query); foreach($books as $book) { $author = $book->getOne('Author'); echo '<br>Author: ' . $author->get('first_name') . ' ' . $author->get('last_name'); echo '<pre>' . print_r( $book->toJSON(), true ) . '</pre>'; }
Propel:
<?php $limit = 500; $books = BookQuery::create() ->orderByTitle() ->limit($limit) ->find(); foreach($books as $book) { $author = $book->getAuthor(); echo '<br>Author: ' . $author->getFirstName() . ' ' . $author->getLastName(); echo '<pre>' . print_r( $book->toJSON(), true ) . '</pre>'; }
Результаты
xPDO:
Total records: 500 Total time: 1.026858 Memory: 4.1216 MB
Propel:
Total records: 500 Total time: 0.102198 Memory: 2.0729 MB
Propel выполнил задачу в 10 раз быстрее чем xPDO, памяти потратил в 2 раза меньше.
Выводы
На сайте xPDO авторы написали, что их творение это "ультра-легкая альтернатива Propel", но на практике на данный момент получается совсем не так. Propel очень существенно обходит xPDO по производительности.