Himberjack
Himberjack

Reputation: 5792

Slow LINQ operation

I am having the following code:

public static IEnumerable<int> GetCloseZipCodes(int zipcode, int radius)
        {
            PycDBDataContext db = new PycDBDataContext();

            ZipCode reqZipInfo = ZipCodes.Instance.zipcodes[zipcode];

            if (reqZipInfo == null)
                return new int[] { };

            double longt = LongitudePlusDistance((double)reqZipInfo.Longitude, (double)reqZipInfo.Latitude, radius);
            double latit = LatitudePlusDistance((double)reqZipInfo.Latitude, radius);
            double minLong = 2 * (double)reqZipInfo.Longitude - longt;
            double minLat = 2 * (double)reqZipInfo.Latitude - latit;

            var zips = ZipCodes.Instance.zipcodes.Where(x => (double)x.Value.Longitude >= minLong &&
                                                  (double)x.Value.Longitude <= longt &&
                                                  (double)x.Value.Latitude >= minLat &&
                                                  (double)x.Value.Latitude <= latit &&
                                                  CalcDistance((double)reqZipInfo.Longitude,
                                                  (double)reqZipInfo.Latitude, 
                                                  (double)x.Value.Longitude, 
                                                  (double)x.Value.Latitude) <= radius).Select(x => x.Key);

            return zips;
        }

This function is given with a zipcode, a radius and it will find all the zipcodes within the radius. I have performance issues here and after running dotTrace most of my time is in the following:

var zips = ZipCodes.Instance.zipcodes.Where(x => (double)x.Value.Longitude >= minLong &&
                                                  (double)x.Value.Longitude <= longt &&
                                                  (double)x.Value.Latitude >= minLat &&
                                                  (double)x.Value.Latitude <= latit &&
                                                  CalcDistance((double)reqZipInfo.Longitude,
                                                  (double)reqZipInfo.Latitude, 
                                                  (double)x.Value.Longitude, 
                                                  (double)x.Value.Latitude) <= radius).Select(x => x.Key);

And third of the time there, it is accessing

public System.Nullable<decimal> Longitude
        {
            get
            {
                return this._Longitude;
            }
            set
            {
                if ((this._Longitude != value))
                {
                    this._Longitude = value;
                }
            }
        }

How can I improve the performance??? We are talking about a few seconds here... I can't understand why. this is all cached and non-SQL.

Upvotes: 1

Views: 142

Answers (1)

Jon Skeet
Jon Skeet

Reputation: 1499860

For one thing, you're doing a lot of conversions between double and decimal as far as I can see.

I'd suggest either changing your object representation to use double to start with, or convert longt and latit to decimal so that you can perform all the comparisons in a single type, without all the conversions.

Aside from anything else, keeping one consistent representation will leave your code cleaner as well as probably performing better. In this case, I'd say that double is probably a more appropriate representation than decimal anyway - it's not like these are exact decimal values like currency; they're naturally-occurring values which have already been approximated by measurement.

Additionally, you're doing the nullable to non-nullable conversion several times, both for x.Value and for comparisons. Consider using a statement lambda instead:

x => {
   if (x.Longitude == null || x.Latitude == null)
   {
       return false;
   }
   double longitude = x.Value.Longitude.Value;
   double latitude = x.Value.Latitude.Value;
   return longitude >= minLong &&
          longitude <= longt &&
          latitude >= minLat &&
          latitude <= latit &&
          longitude >= minLong &&
          CalcDistance(reqZipInfo.Longitude, reqZipInfo.Latitude,
                       longitude, latitude) <= radius;
}

For the sake of readability, I'd probably put this in a separate method to be honest.

(Do you even need the properties to be nullable, by the way? It doesn't make much sense to me.)

Upvotes: 3

Related Questions