Advanced Rails - Building Industrial-Strength Web Apps in Record Time

(Tuis.) #1
Rails Optimization Example | 159

Now, we may want to examine the source of these calls todecode_hex. This function
has only one caller,HexEWKBParser#parse. Clicking on that line takes us to its own
block (see Figure 6-4).


Following this chain of callers upward, we find that these calls are coming from the
spatial_adapterplugin that we use to interface with the PostGIS database. These
functions are doing a particularly computationally intensive form of type casting;
they are converting the hex strings coming from PostGIS to Ruby objects such as
points and polygons (EWKB is theextended well-known binaryformat that PostGIS
uses to represent geometrical objects).


Thedecode_hexfunction is pure Ruby. It could probably be rewritten with C,
OCaml, or another high-performance extension language, which would likely
improve performance substantially. However, we should be considering algorithmic
improvements first.


The first thing that catches my eye about this profile is the number of calls. On the
data set being used, our test query returns slightly fewer than 100 geospatial objects.
So why are we decoding geospatial objects 2,730 times? After some investigation, we
find this note in the spatial adapter’s code:


Because ActiveRecord keeps only the string values directly returned from the data-
base, it translates from these to the correct types every time an attribute is read (using
the code returned by this method), which is probably OK for simple types, but might
be less than efficient for geometries. Also, you cannot modify the geometry object
returned directly or your change will not be saved.

So, every time we reference a geospatial column (viaListing#location), it is being
decoded again from the string representation. We can do better than that. We will
keep a cache of the last-seen string value (the EWKB value from PostGIS), as well as
its corresponding geometry (the Ruby object). This is done by inserting the follow-
ing code into theListing class:


class Listing
# Cache location information
# AR wants to cast the HexEWKB string to a Geometry on each access,
# let's not do that
def location
string = location_before_type_cast
return @location_geometry if string == @location_string

@location_string = string
@location_geometry = super

Figure 6-4. Tracing upward through the call stack

Free download pdf