Dissappointing Yii Performance

Hi, I need an answer from those experienced with implementing Yii. It is our first time to use Yii - we have developed a generic database framework that is database independent and uses EXTJS as a frontend. I know a little about php and webdevelopment but my own background is in desktop-based applications. The programmers made use of Yii because of the features and my concern for speed - Yii claims to be fast.

The first working "version" of the application has been released but it is very slow. It takes about 20 seconds to load a page. I used a debugger to step through the code to see what is happening.

For ANY request issued by the front-end the application loads between 24 and 29 Yii-framework php pages, some of which are more than 1000 lines of code (php parses every line that is loaded, not only those that are executed). It executes a minimum of 650 lines of Yii code. Most of these pages are loaded as a result of the following line in the initial index.php: $peff=Yii::createWebApplication($config);

Is this “normal” for Yii? - I’m sure Yii does many wonderful things but we don’t need all of that to do simple requests… ?

Since the front-end issues multiple REST calls to the backend (e.g. opening a simple page in the application takes more than 40 calls) these 24 - 29 pages are executed more than 40 times. That’s avg. 650 lines of code x a min of 40 calls = 26000 lines of code. (And yes, each of the calls executes the 650 lines of code - I tested).

My request was that the application be capable of handling 1000 concurrent users as it will be installed as a public service on the web. At this level of performance I doubt whether it will handle 100 users (ie. 2,600,000 lines of code).

I am about to call it quits and start over from scratch without Yii. Should I or can this situation be saved?

many thanks for any opinions, help or suggestions.

David

Hi dawieblake,

Welcome to the forum.

While I’m interested in hearing the opinion of someone more used to this kind of problem (I have never had performance issues with Yii), I’d like to help in what I can.

My initial suspect isn’t the backend. All your notes seem completely normal.

I believe that those 40 calls have more to do with your issues.

Could you please check with Firebug or a similar tool how long each request takes?

I’ve never counted the LOC used by Yii when creating a webapp as this isn’t an issue at all in my opinion. There are a lot of things you should check that affect performance:

1.) Do you use an opcode cache (e.g.: APC, XCache etc.)? Using an opcode cache keeps the code in memory and therefore usually boosts performance quite a bit. Take a look at: http://www.yiiframework.com/wiki/312/getting-the-most-out-of-apc-for-yii/

2.) Yii only loads classes that are relevant for handling a user request, but it will also automatically add everything that you manually import via the main configuration (the “import” section in main.php). Only import stuff that is necessary for ALL requests in your app and only import specialized classes by hand within the specific controller actions that are using them. Here’s some information about that: http://www.yiiframework.com/doc/guide/1.1/en/basics.namespace

3.) Do you access a database? If so, could it be the bottleneck? Bad indices, slow queries degrade performance a lot. You can use the debug toolbar extension to show them: http://www.yiiframework.com/extension/yii-debug-toolbar

4.) Do you use data caching? Hitting the API b requesting the same stuff over and over again means unnecessary workload. If you cache data that rarely changes you could serve it directly from within memory (again by using APC cache or something like Memcache). See: http://www.yiiframework.com/doc/guide/1.1/en/caching.data

5.) Last but not least: Bad code can degrade performance but this is beyond the scope of a single thread :)

Btw.: There is nothing Yii uses that could cause 20 second page loads. Nothing. Not even if it would use 10 billion lines of code, seriously. I bet it is the database that is the bottleneck. Maybe a horrible query on 20-30 million rows without indices + not enough RAM for the database to use query caching. That is the worst performance problem

Also, please take into consideration that the browsers usually issue only two parallel requests to the same domain at any given time.

Are you including yiilite.php too, that can help.

In addition to all above…

As stated before Yii tries to load as less as possible classes and only when they are needed… but it has some core classes that are loaded at the start with some configuration processing, environment setting, etc. … let’s call that a Yii app set-up.

Normally when you work with a Yii application the set-up is done only once per request… at the start of the request… than all the job is done and in the end the page is displayed to the user - so to display one-page the set-up is called only once …

As you mentioned above… your "frontend" to display one page makes more than 40 calls to the Yii application… so by that what is happening here is that to display one-page the start-up is done 40 times.

Hi - thanks for a really supporting community!

Rodrigo, I have posted the firebug profile below for the requests - as mentioned the application uses REST - so these are all "API" calls.

Haensal, thanks for your suggestions. All the database queries used in my testing should be simple queries from single tables… so it can’t be that. Is there a function in Yii I can “catch”/log the sql actually being executed?

Maurizio, thanks for the insight… Our frontend is in EXTJS that sends "API" calls via REST… perhaps we should consider a different approach if the scenario you described is the situation… Or is it possible to somehow cache the "environment setup" as you call it?

Perhaps any Yii "gurus" can glean something from the requests below? Let me know if there is anything else I can send?

My current thinking is that since each of the requests below executes all those lines of code it adds up… The question is can we issue less requests (Rodrigo’s comment about the browser only being able to execute two concurrent requests at a time refers), and can the code for these requests be rewritten to require less loading of Yii’s files.

many thanks again,

David


(NOTE: EXTJS debug function is switched-on, which DOES decrease performance, but even with it off, some pages load for more than 30 seconds…

I also replaced / with | below since the forum app does not allow new members to post links (which makes sense)).

GET http:||localhost|peff|peff|js|peff|api|check|provider|grid.json?_dc=1350038276851

200 OK

	893ms	

GET http:||localhost|peff|peff|js|peff|app|controller|provider|grid.js?_dc=1350038278765

200 OK

	1.18s	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|model|provider|edit.js?_dc=1350038280009

200 OK

	934ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|model|provider|grid.js?_dc=1350038280982

200 OK

	700ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|model|providercollectcost_item|grid.js?_dc=1350038281722

200 OK

	1.89s	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|model|providercollectstaff|grid.js?_dc=1350038283669

200 OK

	998ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|view|provider|edit.js?_dc=1350038284705

200 OK

	640ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|view|provider|grid.js?_dc=1350038285379

200 OK

	776ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|ux|grid|GridHeaderFilters.js?_dc=1350038286238

200 OK

	13ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|ext-4.1.3|src|grid|plugin|RowEditing.js?_dc=1350038286297

200 OK

	13ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|ext-4.1.3|src|grid|plugin|Editing.js?_dc=1350038286352

200 OK

	18ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|ext-4.1.3|src|grid|RowEditor.js?_dc=1350038286415

200 OK

	9ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|ux|grid|column|Association.js?_dc=1350038286474

200 OK

	11ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|ux|grid|column|Int.js?_dc=1350038286520

200 OK

	15ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|ux|grid|column|String.js?_dc=1350038286575

200 OK

	12ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|ux|toolbar|ScrollInfo.js?_dc=1350038286620

200 OK

	14ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|ext-4.1.3|src|grid|column|Date.js?_dc=1350038286674

200 OK

	15ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|view|providercollectcost_item|grid.js?_dc=1350038286731

200 OK

	1.59s	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|view|providercollectstaff|grid.js?_dc=1350038288418

200 OK

	949ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|store|provider|edit.js?_dc=1350038289447

200 OK

	299ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|store|provider|grid.js?_dc=1350038289765

200 OK

	306ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|store|providercollectcost_item|grid.js?_dc=1350038290115

200 OK

	361ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|app|store|providercollectstaff|grid.js?_dc=1350038290516

200 OK

	302ms	

ext-debug.js (line 5963)

GET http:||localhost|peff|peff|js|peff|api|provider|grid?_dc=1350038292262&page=1&start=0&limit=60

200 OK

	873ms	

Connec…9646342 (line 309)

Object { header=

"Id"

, dataIndex=

"id"

, filter={…}, more…}

Compon…9648361 (line 39)

Object { header=

"Name"

, dataIndex=

"name"

, editor={…}, more…}

Compon…9648361 (line 39)

Object { header=

"Phone1"

, dataIndex=

"phone1"

, editor={…}, more…}

Compon…9648361 (line 39)

Object { header=

"Phone2"

, dataIndex=

"phone2"

, editor={…}, more…}

Compon…9648361 (line 39)

Object { header=

"Fax"

, dataIndex=

"fax"

, editor={…}, more…}

Compon…9648361 (line 39)

Object { header=

"Website"

, dataIndex=

"website"

, editor={…}, more…}

Compon…9648361 (line 39)

Object { header=

"Address1"

, dataIndex=

"address1"

, editor={…}, more…}

Compon…9648361 (line 39)

Object { header=

"Address2"

, dataIndex=

"address2"

, editor={…}, more…}

Compon…9648361 (line 39)

Object { header=

"Suburb"

, dataIndex=

"suburb"

, editor={…}, more…}

Compon…9648361 (line 39)

Object { header=

"City Town"

, dataIndex=

"city_town"

, editor={…}, more…}

Compon…9648361 (line 39)

Object { header=

"Area Id"

, dataIndex=

"area_id"

, editor={…}, more…}

Compon…9648361 (line 39)

Object { header=

"Notes"

, dataIndex=

"notes"

, editor={…}, more…}

Compon…9648361 (line 39)

Object { xtype=

"toolbar"

, dock=

"top"

, items=[7], more…}

Compon…9648361 (line 39)

Object { id=

"providergrid-add"

, text=

"Add"

, tooltip=

"Add a new row"

, more…}

Compon…9648361 (line 39)

Object { id=

"providergrid-edit"

, text=

"Edit"

, tooltip=

"Edit the selected item"

, more…}

Compon…9648361 (line 39)

Object { id=

"providergrid-delete"

, text=

"Delete"

, tooltip=

"Delete the selected item|items"

, more…}

Compon…9648361 (line 39)

Object { xtype=

"tbfill"

, height=

0

}

Compon…9648361 (line 39)

Object { text=

"Reset Filters"

, tooltip=

"Reset Filters"

, xtype=

"button"

, more…}

Compon…9648361 (line 39)

Object { text=

"Clear Filters"

, tooltip=

"Clear Filters"

, xtype=

"button"

, more…}

Compon…9648361 (line 39)

Object { text=

"Apply Filters"

, tooltip=

"Apply Filters"

, xtype=

"button"

, more…}

Compon…9648361 (line 39)

Object { xtype=

"scrollinfotoolbar"

, store=

"provider.grid"

, dock=

"bottom"

, more…}

Compon…9648361 (line 39)

Object { xtype=

"tbfill"

, height=

0

}

Compon…9648361 (line 39)

Object { xtype=

"tbtext"

, itemId=

"displayItem"

, isContained={…}}

Compon…9648361 (line 39)

Object { xtype=

"tab"

, card={…}, disabled=

false

, more…}

Compon…9648361 (line 39)

Object { isCheckerHd=

true

, text=

" "

, width=

24

, more…}

Compon…9648361 (line 39)

The "start-up" cannot be cached… but you already got some good points above about some caching… schemaCaching should be used too - http://www.yiiframew…Duration-detail

Depending on your Yii code, query caching can help - http://www.yiiframew…a#query-caching

I never used ExtJS so I don’t know how it works… but in the above log there are only calls to JS files… there is no call to any PHP (Yii) file…

Btw, did you check Ext4Yii - http://www.ext4yii.com/

Hi, sorry, here are the list of php files that are opened - roughly in order of occurance:

  1. C:\www\peff\peff\index.php

  2. C:\www\peff\peff\protected\config\main.php

  3. C:\www\peff\peff\protected\config\environment.php

  4. C:\www\peff\peff\protected\config\enviroments\mode_development.php

  5. C:\www\peff\framework\yii.php

  6. C:\www\peff\framework\YiiBase.php

  7. C:\www\peff\framework\base\interfaces.php

  8. C:\www\peff\framework\web\CWebApplication.php

  9. C:\www\peff\framework\base\CApplication.php

  10. C:\www\peff\framework\base\CModule.php

  11. C:\www\peff\framework\base\CComponent.php

  12. C:\www\peff\framework\collections\CMap.php

  13. C:\www\peff\framework\collections\CAttributeCollection.php

  14. C:\www\peff\framework\logging\CLogger.php

  15. C:\www\peff\framework\logging\CLogRouter.php

  16. C:\www\peff\framework\base\CApplicationComponent.php

  17. C:\www\peff\framework\logging\CFileLogRoute.php

  18. C:\www\peff\framework\logging\CLogRoute.php

  19. C:\www\peff\framework\logging\CProfileLogRoute.php

  20. C:\www\peff\framework\logging\CWebLogRoute.php

  21. C:\www\peff\framework\collections\CList.php

  22. C:\www\peff\framework\web\CHttpRequest.php

  23. C:\www\peff\framework\web\CUrlManager.php

  24. C:\www\peff\framework\caching\CFileCache.php

  25. C:\www\peff\framework\caching\CCache.php

  26. C:\www\peff\peff\protected\controllers\ExtappController.php

  27. C:\www\peff\peff\protected\components\ext\abstract\AbstractAppController.php

  28. C:\www\peff\peff\protected\components\Controller.php

  29. C:\www\peff\framework\web\CController.php

  30. C:\www\peff\framework\web\CBaseController.php

  31. C:\www\peff\framework\web\CHttpSession.php

  32. C:\www\peff\framework\web\actions\CInlineAction.php

  33. C:\www\peff\framework\web\actions\CAction.php

  34. C:\www\peff\peff\protected\extensions\mustache\components\MustacheApplicationComponent.php

  35. C:\www\peff\peff\protected\extensions\mustache\vendors\Mustache.php

  36. C:\www\peff\peff\protected\components\pef\Pef.php

  37. C:\www\peff\peff\protected\components\pef\PefBase.php

  38. C:\www\peff\framework\collections\CListIterator.php

Are all those javascript files loaded on one page?

i think i know what your problem is, and that is code refactoring.

To as i wud like to know if you were a java desktop programmer? if so

i wud strongly advice you refactor your code.

yes they are.

Hi… this list of files are loaded when a page is opened - is it “normal”? This page has many relationships. My index-finger actually gave-up stepping through code after about 700 clicks, so there may be more. (For an old-school programmer that strives to optimize code, this list seems really excessive. Imagine if 1000 users concurrently issue requests). To answer the previous person’s post, no I used programmers with sufficient web-development experience, but Yii was new to them.

  1. C:\www\peff\peff\index.php

  2. C:\www\peff\peff\protected\config\environment.php

  3. C:\www\peff\peff\protected\config\main.php

  4. C:\www\peff\peff\protected\config\enviroments\mode_development.php

  5. C:\www\peff\framework\yii.php

  6. C:\www\peff\framework\YiiBase.php

  7. C:\www\peff\framework\base\interfaces.php

  8. C:\www\peff\framework\web\CWebApplication.php

  9. C:\www\peff\framework\base\CApplication.php

  10. C:\www\peff\framework\base\CModule.php

  11. C:\www\peff\framework\base\CComponent.php

  12. C:\www\peff\framework\collections\CMap.php

  13. C:\www\peff\framework\collections\CAttributeCollection.php

  14. C:\www\peff\framework\logging\CLogger.php

  15. C:\www\peff\framework\logging\CLogRouter.php

  16. C:\www\peff\framework\base\CApplicationComponent.php

  17. C:\www\peff\framework\logging\CFileLogRoute.php

  18. C:\www\peff\framework\logging\CLogRoute.php

  19. C:\www\peff\framework\logging\CProfileLogRoute.php

  20. C:\www\peff\framework\logging\CWebLogRoute.php

  21. C:\www\peff\framework\collections\CList.php

  22. C:\www\peff\framework\web\CHttpRequest.php

  23. C:\www\peff\framework\web\CUrlManager.php

  24. C:\www\peff\framework\caching\CFileCache.php

  25. C:\www\peff\framework\caching\CCache.php

  26. C:\www\peff\peff\protected\controllers\ApiController.php

  27. C:\www\peff\peff\protected\components\ext\abstract\AbstractCrudController.php

  28. C:\www\peff\peff\protected\components\ext\ExtStoreController.php

  29. C:\www\peff\peff\protected\components\Controller.php

  30. C:\www\peff\framework\web\CController.php

  31. C:\www\peff\framework\web\CBaseController.php

  32. C:\www\peff\framework\web\CHttpSession.php

  33. C:\www\peff\framework\web\actions\CInlineAction.php

  34. C:\www\peff\framework\web\actions\CAction.php

  35. C:\www\peff\peff\protected\components\PInsureConvention.php

  36. C:\www\peff\peff\protected\components\PDbSettings.php

  37. C:\www\peff\framework\db\CDbConnection.php

  38. C:\www\peff\framework\db\CDbCommand.php

  39. C:\www\peff\peff\protected\components\PContainer.php

  40. C:\www\peff\framework\db\schema\CDbCriteria.php

  41. C:\www\peff\peff\protected\components\pef\models\Container.php

  42. C:\www\peff\framework\db\ar\CActiveRecord.php

  43. C:\www\peff\framework\base\CModel.php

  44. C:\www\peff\framework\db\schema\mysql\CMysqlSchema.php

  45. C:\www\peff\framework\db\schema\CDbSchema.php

  46. C:\www\peff\framework\db\schema\mysql\CMysqlTableSchema.php

  47. C:\www\peff\framework\db\schema\CDbTableSchema.php

  48. C:\www\peff\framework\db\schema\mysql\CMysqlColumnSchema.php

  49. C:\www\peff\framework\db\schema\CDbColumnSchema.php

  50. C:\www\peff\framework\db\schema\CDbCommandBuilder.php

That’s perfectly fine. Have a look at those files if you want: You’ll see that they are relatively small.

And on top of what’s been proposed before: Counting SLoCs is a poor metric for performance. Not every line will translate into the same amount of opcodes. In the case of virtually all interpreted languages, the number of lines being run becomes even meaningless as soon as an opcode cache is involved.

try ext-all.js

Hi Da Sourcerer,

thanks for your insight… it is helpful, but the little I understand of caching says it limits us to clients that have root access to their servers (I particularly want to help NPO’s that often make use of shared hosting services).

This is my last post for today, as new members are only allowed 3 posts a day… Id like to pose some questions in view of your comment:

  1. Should opcode cache make the amount of lines of code irrelevant, it invalidates Yii’s claims to being the fastest framework as another framework may be completely bloated with 100x more code but the opcode cache makes them even… Yet, Yii claims to be much faster regardless.

  2. It makes sense to my childlike thinking, based on first principles, that less code and less iterations = faster applications. If all I need is 10 lines of code to perform an update statement, why should I go through 1000 ?

  3. The avg size of those php files are roughly 400 lines - some exceed 1000 lines. I can see why a opcode cache will make such a big difference as each and every line is parsed by php, and if the application makes 40 calls to the php server for a single page load it adds up… Should we not be able to go the caching route due to shared hosting restrictions, should we get rid of REST and API calls and issue one call instead… ?

I have received a lot of help on this forum - will definitely take these ideas further. I am still though a bit amazed / naïve that the vast amount of code that is executed for even the very simplest requests is not seen as an issue at all… I’ll take your word for it, but would like to understand :).

many thanks

Why does it invalidate Yii’s claims? 1 line of badly written code can be worse than 1000 good written LOC. I don’t know why you are so focused on that particular point.

You are using a PHP framework because you want a toolset that helps you to solve complex every day problems (like database abstraction via ActiveRecord). Yes, you could probably use 10 lines of code of pure PHP for the same task but what about security, db abstraction, error logging, scalability etc etc. You would lose all these benefits as Yii does them automatically for you in those other 1000 lines of code.

I suppose you are creating a single page app (loading in data via REST and AJAX). While a lot of people think it is the holy grail even Twitter considered this to be a problem as you can’t control performance on the client (browser), only your servers, so they switched back to server side rendering a few months ago. An alternative to the REST/JSON approach could be PJAX: https://github.com/defunkt/jquery-pjax. It feels snappy etc but the rendering is done server side AND your content is SEO friendly (would even work without javascript). Just my two cents.

I think you’re struggling to understand the difference between opcode caching and actual data caching as performed by the application.

Don’t lket that get through you. As most forums, we’re suffering quite a bit from spammers. This limitation is one of the countermeasures.

It would be interesting to know with which languages you’ve worked so far. Given that you said you had a background in desktop app development, I’ll blindly assume there’s at least one compiled language. So just ask yourself: Into how many bytes of machine-readable code do your sourcefiles turn after the compile? If you’re using a language close to the hardware such as C, you could probably give an estimate if your project is really, really small. But what if it’s larger? What if you’ve decided to let your compiler perform some optimizations?

Same here: There are quite a lot of optimizations PHP performs in the background. The process of lexing/parsing PHP sourcefiles and turning them into intermediate bytecode is quite fast, so it’s usually not our concern. Opcode caches accelerate this even further as they spare the PHP environment the lexing/parsing and optimization part. All of this leads to the conclusion that it is of little use to just stare at the sheer number of lines in a sourcefile. Besides: There are a lot of extensions and helpers in PHP that one doesn’t necessarily notices. The cost in opcodes of a line is anything but fixed. Any idea what the cost of mysqli_connect() is? No? Me neither :)

I’ve got an intersting theory: I believe there are a lot of widgets on your site that cannot or do not fetch the data they need from the initial page. Instead they are issueing a call via REST to fetch their initial dataset. If that is the case, you’re facing a design issue.

This might indicate that you have absolutely no idea what it takes your webbrowser to issue those requests. Let alone the underlying code in the OS, the driver of your NIC and finally the targeted webserver ::)

had the same problem, loading a page took about 20 seconds. It’s not Yii, it’s ExtJS.

At the risk of sounding harsh…

The OP seems to know what he’s talking about. Personally the tone is not one of a Yii beginner, but a Yii basher.

Yii is proven to be fast. Yup there’s some leaner frameworks arrived in the last year or so, but damn… Yii doesn’t drag it’s feet!

OP has other issues, unrelated to Yii, or is using the wrong tool.