Psyco is strictly operational while Python is running. That is, the Python source code is compiled into bytecode through the Python command, in exactly the same way as before (except for several import statements and function calls that were added to invoke Psyco). But when the Python interpreter runs the application, Psyco checks to see if it can replace the regular Python bytecode operation with some specialized machine code. This specialized compilation is very similar to what the Java Just-in-time compiler does (generally, at least) and is architecture-specific. Until now, Psyco can only be used for i386 CPU architectures. The beauty of Psyco is that you can use the Python code you've been writing (exactly the same!). , but it can make it run faster.
How Psyco is working.
To fully understand Psyco, you may need to have a good grasp of the eval_frame () function and i386 assembly language of the Python interpreter. Unfortunately, I cannot give an expert opinion on either of them-but I think I can outline Psyco roughly.
In conventional Python, the Eval_frame () function is the inner loop of the Python interpreter. The Eval_frame () function mainly looks at the current bytecode in the execution context and switches the control outward to a function that is appropriate for implementing the bytecode. The specifics of what the support function will do usually depend on the state of the various Python objects that are stored in memory. To put it simply, adding Python objects "2" and "3" and adding Objects "5" and "6" will produce different results, but both operations are assigned in a similar way.
Psyco replaces the Eval_frame () function with a compound evaluation unit. There are several ways that Psyco can be used to improve the operations that Python does. First, Psyco compiles the operation into a somewhat optimized machine code, and because the machine code needs to do what the Python assignment function does, it only has a slight improvement. Furthermore, the "dedicated" content in Psyco compilation is not only a choice of Python bytecode, Psyco also specialization the values of variables known in the execution context. For example, in code similar to the following, variable x is known for the duration of a loop:
Copy Code code as follows:
x = 5
L = []
For I in range (1000):
L.append (X*i)
The optimized version of this code does not require "x variable/object content" to multiply by each I, compared to the simple use of 5 multiplied by each I for less overhead, omitting the lookup/indirect reference step.
In addition to creating i386-specific code for small operations, Psyco caches this compiled machine code for future reuse. If Psyco can recognize a particular action as it did earlier ("specialized") operations, it can rely on this cached code without having to compile the snippet again. This saves a little time.
However, the real time-saving reason in Psyco is that Psyco divides operations into three different levels. For Psyco, there are "Run-time", "compile-time" and "Virtual time" variables. Psyco to increase and decrease the level of variables as needed. Run-time variables are simply raw bytecode and object structures that are processed by the regular Python interpreter. Once the Psyco is compiled into machine code, the compile-time variables are represented in machine registers and directly accessible memory locations.
The most interesting level is the virtual time variable. Internally, a Python variable is a complete structure with many members-even when the object represents only an integer. Psyco virtual time variables represent Python objects that might be built when needed, but the details of these objects are ignored before they become Python objects. For example, consider the following assignment:
x = 15 * (14 + (13-(12/11)))
Standard Python constructs and destroys many objects to compute this value. Construct a complete integer object to hold (12/11) this value, and then "pull" a value from the structure of the temporary object and use it to compute the new temporary object (13-pyint). Instead, Psyco skips these objects and evaluates only those values, because it knows "if needed," you can create an object from a value.
Using Psyco
Explaining Psyco is relatively difficult, but using Psyco is very easy. Basically, the whole thing is to tell the Psyco module which function/method to "specialize". No code for the Python function and the class itself needs to be changed.
There are several ways to specify what Psyco should do. The "Shotgun (Shotgun)" method allows Psyco instant operations to be used everywhere. To do this, place the following lines at the top of the module:
Copy Code code as follows:
Import Psyco; Psyco.jit ()
From psyco.classes Import *
The first line tells Psyco to "play its magic" on all global functions. The second line (in Python 2.2 and above) tells Psyco to perform the same action on the class method. To more precisely determine the behavior of Psyco, you can use the following command:
Psyco.bind (SomeFunc) # or method, class
newname = Psyco.proxy (func)
The second form takes Func as a standard Python function, but optimizes calls involving NewName. In almost all cases except testing and debugging, you will use the Psyco.bind () Form.
Performance of Psyco
Although Psyco is so magical, using it still requires a little thought and testing. The main point is to understand that Psyco is useful for handling blocks of multiple loops, and it knows how to optimize operations involving integers and floating-point numbers. For operations that are not loop functions and other types of objects, Psyco mostly only increases the overhead of parsing and internal compilation. Also, for applications that contain a large number of functions and classes, enabling Psyco throughout the application scope adds a significant burden to machine code compilation and memory usage for this cache. It is much better to selectively bind to functions that derive maximum benefit from Psyco optimizations.
I started my testing process in a very naïve way. I've only considered applications that I've run recently, but have not yet considered accelerating. The first example that comes to mind is a text operation that converts my forthcoming manuscript (Text processing in Python) into a LaTeX format. The application uses some string methods, some regular expressions, and some program logic that is driven primarily by regular expressions and string-matching. Actually it was a bad choice to use it as a Psyco test candidate, but I used it, and it started.
The first time in the test, all I did was add Psyco.jit () to the top of the script. It's not a bit of a struggle. Sadly, the result (as expected) was disappointing. The original script took 8.5 seconds to run, and it probably ran for 12 seconds after the Psyco "acceleration". How lame! I guess the startup overhead required for Just-in-time compilation is a drag on running time. So next I try to process a larger input file (consisting of multiple copies of the original input file). This time it was a small success, reducing the running time from about 120 seconds to 110 seconds. The acceleration effect in several runs is consistent, but the effect is not significant.
The second pass test for this process candidate. Instead of adding a total Psyco.jit () call, I just added the Psyco.bind (main), because the main () function does have to loop more than once (but only the smallest integer operation is used). The results here are nominally better than the front. This method reduces the normal running time by a very few seconds, and in the case of a larger input version, it cuts for a few seconds. But the results have not yet been brought to the attention (but they have not harmed).
For more appropriate Psyco testing, I searched for some of the neural network code I wrote in my previous article (see Resources). This code recognizer (Code_recognizer) application can be "trained" to identify possible distributions of different ASCII values written in different programming languages. Something like this might be useful in guessing the type of file (for example, a lost network packet), but the code is actually completely generic in terms of "training"-it's easy to learn to recognize faces, sounds, or tidal patterns. In any case, the code recognizer is based on the Python library Bpnn,psyco 4.0 distribution, which also contains (in the form of a correction) the library as a test case. In this article, the code recognizer is focused on knowing that it does a lot of floating-point arithmetic loops and takes a long time to run. Here we have a good candidate case for Psyco testing. After using the
for a while, I set up some more details about Psyco usage. For applications with only a few classes and functions, there is not much difference between using instant binding or target binding. But the best result is that you can still get a few percent improvement by selectively binding the optimization class. However, it is important to understand the scope of the Psyco binding. The
code_recognizer.py script includes these lines similar to the following:
Import nn from Bpnn
class NN2 (NN):
# Customized output methods, math core inherited
that is, from the Psyco point of view, interesting things in the class BP In the midst of nn.nn. adding Psyco.jit () or Psyco.bind (NN2) to the code_recognizer.py script does not work. For the Psyco to perform the desired optimizations, you need to add Psyco.bind (NN) to code_recognizer.py or add Psyco.jit () to bpnn.py. In contrast to what you might assume, instant optimization occurs not when an instance is created or when the method is run, but within the scope of the definition class. In addition, bound derived classes do not specialize their methods that inherit from other places.
Once the fine details of the appropriate Psyco binding are found, the acceleration effect is quite obvious. Using the same test cases and training methods provided in the reference article (500 training modes, 1000 training iterations), the neural network training time has been reduced from about 2000 seconds to about 600 seconds-speed up by more than 3 times times. The number of iterations is reduced to 10, and the multiples of the acceleration are scaled down (but the recognition capability for the neural network is not valid) and the intermediate values of the iterations change.
I find that using two lines of new code can reduce the elapsed time from more than half an hour to about 10 minutes, and the effect is very significant. This acceleration may still be slower than a similar application written in C, and it is certainly slower than the 100 times-fold acceleration that is reflected by several independent Psyco test cases. But this application is quite "real", and in many environments these improvements are already significant enough.