With some new classes in Grantlee::Tubes starting to take shape, I started making use of them in the new Grantlee::Thermodynamics library with the Grantlee::Refridgeration system.
Grantlee::Refrigeration makes use of components from Grantlee::Tubes, like Grantlee::Pump, QtIOCompressor and Grantlee::Cat to create an ideal heat transfer system. The intention is to create a stream of “cold bytes” taking heat out of hot paths in your code, and disposing of the heat through your cooling fan.
Coolness. You can't get enough
Thermodynamics teaches us that while the quantity of energy in a closed system is constant, the quality of the energy (it’s entropy) is not. Entropy in a closed system is always increasing (the quality or useful energy in the universe is always going down), but we can locally decrease the entropy in a body of mass if we transfer it to another body of mass. This is what refrigeration is about. The decrease in entropy is made visible in the cold cavity of a fridge by the state change that water undergoes from fluid (higher entropy) to solid (lower entropy).
We can take heat (enthalpy and entropy) away from somewhere, but we have to dump that heat somewhere else. It takes work and a refrigerant to transfer heat between thermodynamic bodies. Heat won’t move spontaneously by itself (beyond the limits of equilibrium) so typically the work of heat transfer is done by a pump. Heat transfer in a refrigerator works in a cycle of four stages.
Grantlee already provides a Pump which we can use in our thermodynamic system, and we can use any refrigerant which is plentiful and which has a high capacity for entropy and a lot of hot air, such as a twitter feed.
We start by seeding the system with our refrigerant.
QNetworkReply *reply = QNetworkAccessManager::get("http://www.twitter.com/feed");
Grantlee::Cat *cat = new Grantlee::Cat;
Cat will read the data from the reply object until it closes, at which point it will start to read from another device to close the cycle (shown later). At this point the data is already saturated; It can’t contain any more entropy at this temperature and pressure.
1 – 2: Reversible Adiabatic (Isentropic) Compression
Typically the first step described in a refrigeration cycle is Isentropic compression – that is, compressing the refrigerant without changing its entropy. The compression causes the data to become super-saturated. We compress the data by tubing the refrigerant through a QtIOCompressor.
// (condenser shown later)
Condenser *condenser = new Condenser;
QtIOCompressor *compressor = new QtIOCompressor(condenser);
2 – 3: Constant Pressure Heat Rejection
After compression comes constant pressure heat rejection. As all developers know, constraints of constness can be expressed in C++ with the const keyword. So we require a class for rejecting the heat which will enforce that constraint:
class Condenser : public QIODevice
void writeData( const char* data, qint64 len );
Fortunately, the virtual writeData method of QIODevice already takes the data (our refrigerant) as const, so that constraint is already enforced for us. The condenser causes a change of state of the data, thereby decreasing its entropy. The data is once again saturated, but now in its low entropy state and at a lower temperature.
3 – 4: Adiabatic Throttling
We now have to connect up a throttle to perform isentropic expansion, so the entropy and the temperature is maintained, but the refrigerant changes state and becomes unsaturated.
A throttle is trivially implemented by using a QtIOCompressor in reverse, so we omit that for brevity.
At this point, we have our stream of cold bytes at a low temperature and unsaturated, so with a capacity to absorb some heat, so let’s do that with an evaporator.
4 – 1: Constant Pressure Heat Addition
We require that heat absorption occurs at constant pressure, and once again the const keyword ensures that.
class Evaporator : public QIODevice
void writeData( const char* data, qint64 len );
(The implementation of the Evaporator is left as an exercise for the reader)
We can then use the cold bytes in the method that defines our hot code path where we use an evaporator to facilitate the heat transfer to the refrigerant:
void MyClass::hotPath(..., QIODevice *source, QIODevice *target)
Evaporator *evaporator = new Evaporator;
The very presence of the evaporator in the hot path of our code is enough to cause heat transfer to the cold bytes, increasing their entropy by causing them to change state.
Of course this means that we need to call our hot path with the refrigerant tubing included:
Grantlee::Channel *channel1 = new Grantlee::Channel;
myInstance->hotPath(..., throttle, channel1);
Grantlee::Pump *pump = new Grantlee::Pump;
Grantlee::Channel *channel2 = new Grantlee::Channel;
As a result of the state change in the evaporator the data also becomes saturated at high entropy. Recall that this is the same state the refrigerant we originally introduced from twitter was in.
We route the refrigerant from the hot path and into a Grantlee::Pump, which provides the work required to satisfy the Second Law, and then forwarding the result on to cat, thereby closing the cycle.
I ran some tests using the Grantlee Thermodynamics Toolkit on various algorithms with various parameters of compression and throughput, with results indicating a universal increase in performance when refrigeration was used.