sooprise
sooprise

Reputation: 23207

Improving The Performance Of My Application?

I have a database table that contains a list of tasks. I have a program that counts the number of tasks that are running at every minute for a specified time period.

I have one query that grabs all of the results for the specified time period, and another linq query in a loop that grabs all of the results in each one minute interval.

That's all well and good, but now I'm having difficulty improving the performance of my code.

for(int i=0;i<oneMinuteIntervals;i++){
    var resultsThisMinute = 
    from fullResult //this contains all of the tasks in the whole period
    where //task is running during this one minute interval

    foreach(var result in resultsThisMinute){
        //Does stuff
    }
}

It's taking about 33 milliseconds on the foreach loop even if resultsThisMinute is empty. I tried adding a if(resultsThisMinute.Count() == 0) but that takes as long as the for loop. There are many occasions where there are 0 tasks in a given minute, so I wish there was a quicker way to check for this.

If there is, please post, I really appreciate it!

Upvotes: 1

Views: 95

Answers (3)

Amy B
Amy B

Reputation: 110221

The problem is you are iterating fullResult completely for each loop iteration. This isn't needed. Instead you can simulate a clock and see which tasks are active as you move through time.

Assuming your tasks have a StartTime, an EndTime and a unique Id... (untested)

Queue<Task> starts = new Queue(fullResult.OrderBy(task => task.StartTime));
Queue<Task> ends = new Queue(fullResult.OrderBy(task => task.EndTime));

Dictionary<int, Task> activeTasks = new Dictionary<int, Task>();

for(int i=0;i<oneMinuteIntervals;i++)
{ 
  DateTime current = ComputeDateTime(i);
    // may be needed
  // current = current.AddMinutes(1);

  while(starts.Any() && starts.Peek().StartTime < current)
  {
    Task startingTask = starts.Dequeue();
    activeTasks[startingTask.Id] = startingTask;
  }

  foreach(Task result in activeTasks.Values)
  {
    //Does stuff
  } 

  while(ends.Any() && ends.Peek().EndTime < current)
  {
    Task endingTask = ends.Dequeue();
    activeTasks[endingTask.Id] = null;
  }
}

Also - make sure there's no data access occuring in the //Does Stuff part. That would slow you down greatly.

For the mathematically inclined, I would characterize the original's execution time as t * n + ?, which is simple nested looping where t is the number of minutes, n is the rowcount in fullResult and ? is the execution time of enumerating the active tasks after they are found.

My code would be 2 * (n log n) + 2 * n + ? , which is two sorts and two full iterations.

Upvotes: 2

Bennor McCarthy
Bennor McCarthy

Reputation: 11675

Unless you're calling .ToList() or similar before your foreach loop, you're not actually doing the database lookup until you start enumerating the results in the loop. That's likely to be where the 33 milliseconds is coming from, and if that's the cause, it's neither avoidable nor something to worry about: it's pretty quick for a database query.

I'm assuming that there's something in your loop to delay for a minute between each iteration, in which case there's no need to avoid the lookup per iteration overhead.

[Edit]

Just noticed you're pulling the results from another query. You'll want to make sure the fullResults variable contains the result of a ToList() or ToArray() to avoid hitting the database every time you pull results from it.

Upvotes: 1

Dan Hass
Dan Hass

Reputation: 67

Your query is making oneMinuteIntervals calls to the database. You might be able to group by the minute and add time as a key to get this down to a single database call. So you would have a data structure like

Dictionary<Datetime, fullResult>

And populate the dictionary with a single database call.

Upvotes: 0

Related Questions