In this article we will talk about some of the sometimes overlooked aspects of developers that affect the overall performance of a web application. In particular, let us consider how the performance of multiple connections of external files, the presence of dead code, acceleration through the cache of the opcode and FastCGI for PHP affects the performance.
To some extent, the article is a consequence of the topic
“Storing the code in the database or building the code block by brick” , since it was after him that there were multiple questions about performance (by the way, not so long ago,
another project based on this technology of storing all the code in the database started) . One of the advantages of the considered approach was the possibility of obtaining at the output a pure monolithic code that does not contain “dead” sections and connections of external files (for example, by means of
include or
require . Further, for simplicity, we call this simply “incloud”). For those who are not familiar with the topic, I will explain that the code is “dead”, which will not be executed on the page.
A bit of science. Here is what the director of the Institute of System Design of the Russian Academy of Sciences, Corresponding Member of the Russian Academy of Sciences, one authoritative uncle Viktor Ivannikov, tells about the dead code:
')
“A dead code is such a part of a program or circuitry that is never, under any conditions, under any input data being executed. This is a significant problem, especially for systems with limited memory. Dead code can take up to 30% of the program, especially it turns out a lot with the development of applications, because they bring in more and more new pieces. The dead code problem is algorithmically unsolvable . This means that I can not create a tool that would find dead code for any program.
This problem became interested in the early 50s. The corresponding Rice theorem and the Ouspensky theorem were proved simultaneously and independently, only Vladimir Andreevich can be formulated more generally. Theorems actually state that recognition of any non-trivial property of an algorithm is an insoluble problem. ”
From myself I will add that in web programming the amount of dead code is usually an order of magnitude larger. With a swap connection, its share easily exceeds the threshold and is 80-90%. That is, only every 20 line of code will actually be executed. This is especially clearly seen when using "heavy" frameworks.
Here we introduce a couple of definitions that we will use below:
monolith - a code that does not use external file connections (inclusions)
pure monolith is a monolith in which there is no dead code.
As stated, pure monolithic code significantly increases the overall performance of the application, which caused a lot of controversy and suspicion in the comments. Fairly, there were questions about how “dead” code and all applications slow down applications? And as a solution to problems with the performance and purity of the code, the
opcode caches were mentioned, the “magic” functions of autoconnection of classes and methods (hereinafter, for simplicity we will call this simply
autoload ), as well as
fastCGI .
Well, let's try to sort out the question in search of truth. So let's go.
Is there a problem?
To begin with, let's try to figure out how generally the dead code and the inclusions affect performance.
Let's start with the dead code. Create pages for 2000, 5000, 10000 lines of code and set the apacheBenchmark on them. We get the following results:
number of lines | requests / sec | average request time | dead code rate |
---|
2000 | 72.24 | 0.013 | 0 |
5000 | 32.20 | 0.031 | 0.6 |
10,000 | 15.97 | 0.062 | 0.75 |
Hereinafter, the time obtained on an old FS Amilo 1718 laptop is indicated. The results of control tests conducted on different systems with different OS and FS did not differ in proportion
As you can see, the time spent on processing the script is actually directly proportional to the number of lines in it (which is quite logical, since we do not do anything in the script, most of the time is spent on its parsing. More lines - correspondingly more parsing time). How big is this time? It is
significant . Agree, even ideally, in the absence of a dead code, 13 thousandths are a lot (not to mention 62, as in the third test). During this time, you can give the finished page by doing a connection to the database, requests, and so on. There is only parsing ...
Now let's deal with the inclusions. We will simulate 4 situations:
- "monolith" (without inclodes and dead code)
- "swap" (Simulates the unconditional connection of all libraries, which are usually combined into large files. It involves
few large files . A lot of dead code)
- “autoload” (Simulates connecting classes / methods / functions if necessary (usually at the time of accessing a nonexistent class / method / function). It entails
many small files . Almost no dead code)
- A couple of intermediate states between a swap and autoload.
Just for fun To complete the picture :)
The results are shown in the following table (in performance order):
number of lines | inclodes | requests / sec | average request time | dead code rate | imitation type |
---|
2000 | 0 | 72.24 | 0.013 | 0 | pure monolith |
2000 | 60 | 35.12 | 0.028 | 0 | autoload |
5000 | 7 | 25.63 | 0.039 | 0.6 | - |
10,500 | 14 | 13.2 | 0.076 | 0.81 | - |
15,000 | five | 9.78 | 0.1 | 0.87 | swap |
As expected, in the first place is a monolith, in the second with an almost two-fold lag autoload. Well, the swap nervously smokes on the sidelines :) (The truth is only for now, then he will kick the ass off the car. Read the article further). But the time to process the request is still very long ...
By the way, this table just confirms the number I indicated in the received gain of 600% when the system is transferred to a pure monolith.
There is a problem - there is a solution
The main means of solving problems and increasing application performance by reducing the cost of parsing pages are cache bytes of code. The most well-known caching systems (in PHP, for example) are
eAccelerator ,
xCache and
APC . For testing, eAccelerator was chosen as the most common and fastest in terms of our task. In some detail, comparing caching systems can be found
here . Here are the results using the opcode cache (in order of performance):
number of lines | inclodes | requests / sec (without opcode cache) | requests / sec with opcode cache | acceleration factor | imitation type |
---|
2000 | 0 | 72.24 | 412.86 | 5.71 | pure monolith |
15,000 | five | 9.78 | 204.6 | 20.92 | swap |
10,500 | 14 | 13.2 | 192.2 | 14.56 | - |
2000 | 60 | 35.12 | 112.11 | 3.19 | autoload |
And here we see very interesting facts. When using the opcode cache, despite the huge amount of dead code, the
swap connection is almost 2 times more efficient than autoload ! And all thanks to the insane acceleration rate of 21x. But with autoload, the acceleration factor turned out to be the lowest - the cache of the opcode only 3 times reduced costs. From here we make an uncomplicated conclusion: the opcode cache practically solves the problem of a “dead” code, but
does not solve the problem of inclusions. Keshera also do not like a large number of small files, showing them the smallest rate of performance increase.
What about FastCGI?
According to many, another way to speed up scripts and reduce the cost of "loading" the script is running PHP in FastCGI mode.
This is not true! . Why fastCGI does not speed up PHP, you can read
here Koterov. By the way, his eAccelerator tests were also conducted there using the example of the
Zend Framework . The results obtained by Dmitry Koterov are very close to those given in this topic.
Conclusion
The purpose of the article was to give a detailed look at the effect of the dead code and the connection of external files in the code to the final performance of the application. As you can see the influence of these factors is quite significant.
The article is written by
Napolsky , for
obvious reasons, it could not publish it. All + in his karma;)