У меня есть следующий алгоритм для расчета определенных соотношений «работа», «низкая работа» и «отсутствие работы» в течение установленного диапазона дат (startDate
, endDate
). Алгоритм работает нормально, но когда я увеличиваю диапазон дат, он, очевидно, работает медленнее, потому что ему приходится перебирать каждый день этого диапазона каждую минуту. Есть ли способ улучшить производительность с помощью другого типа цикла или предложения where, чтобы найти текущую дату в listWorkTime
.
private List<DateSharedWork> CalculateDateSharedWork(DateTime startDate, DateTime endDate, ICollection<WorkTime> listWorkTime)
{
List<DateSharedWork> listDateSharedWork = new List<DateSharedWork>();
// +1 to include last day at full
int range = endDate.Subtract(startDate).Days + 1;
// start at startDate
Parallel.For(0, range, i =>
{
DateTime currDate = startDate.AddDays(i);
//set minute interval
double everyNMinutes = 1.0;
double minutesADay = 1440.0;
// reset counter
int work_counter = 0;
int lowWork_counter = 0;
int noWork_counter = 0;
int l = (int)(minutesADay / everyNMinutes);
for (int j = 0; j < l; j++)
{
DateTime check15 = currDate.AddMinutes(j * everyNMinutes);
// check if listWorkTime includes current date
var foundTime = listWorkTime.Where(x => check15 >= x.FromDate && check15 <= x.ToDate).ToList();
if (foundTime.Count(x => x.TimeRangeId == 1) > 0)
{
// found interval that is within work hours
work_counter++;
noWork_counter++;
}
else
{
if (foundTime.Count(x => x.TimeRangeId == 2) > 0)
{
// found intervall that is within low work hours
lowWork_counter++;
noWork_counter++;
}
}
};
double work = everyNMinutes / minutesADay * work_counter;
double lowWork = everyNMinutes / minutesADay * lowWork_counter;
double noWork = 1.0 - (everyNMinutes / minutesADay * noWork_counter);
listDateSharedWork.Add(new DateSharedWork(currDate, work, lowWork, noWork));
});
listDateSharedWork.Sort((x, y) => DateTime.Compare(x.Date, y.Date));
return listDateSharedWork;
}
Редактировать
определения классов DateSharedWork и WorkTime
public class DateSharedWork
{
public DateSharedWork(DateTime date, double? work = 0.0, double? lowWork = 0.0, double? noWork = 1.0)
{
this.Date = date;
this.Work = work.Value;
this.LowWork = lowWork.Value;
this.NoWork = noWork.Value;
}
public DateTime Date { get; private set; }
public double Work { get; private set; }
public double LowWork { get; private set; }
public double NoWork { get; private set; }
}
public class WorkTime
{
public int Id { get; set; }
public DateTime? FromDate{ get; set; }
public DateTime? ToDate{ get; set; }
public int? TimeRangeId{ get; set; }
}
Edit2
начиная с startDate
значение как первый день и увеличивая по минутной шкале в течение всего дня, я получаю отношения нормальная работа а также низкая работа на тот день под флагом TimeRangeId
,
listWorkTime
содержит информацию, когда определенный период был либо рабочим, либо малым, эти периоды могут перекрываться, а также дублироваться, но нормальная работа (TimeRangeId == 1
) доминирует
1 ответ
Всего несколько быстрых снимков …
Вместо того, чтобы иметь
DateTime check15
что приводит к предположению, что может быть еще несколькоDateTime
структуры, лежащие вокруг, такие как check1 … check15, вы могли бы просто использоватьcurrDate
который, кстати, следует переименовать вcurrentDate
потому что сокращения затруднят чтение кода. ИспользуяcurrentDate
ты мог бы заменить(j * everyNMinutes
вcurrDate.AddMinutes()
просто1.0
или лучшеeveryNMinutes
.foundTime
как имя дляList
не лучший выбор в качествеList
подразумевает более одного элемента. Как насчетfoundTimes
? Но гораздо важнее то, что каждую минуту код повторяетсяlistWorkTime
в худшем случае дважды. Если вы используете толькоCount()
чтобы проверить, есть ли любой элемент найден, тогда просто используйтеAny()
.Count()
будет перебирать все элементы, покаAny()
останавливается на первом найденном элементе.Вы должны использовать профилировщик, чтобы проверить, удаляет ли
ToList
послеWhere()
получит больше производительности.идеоматический способ именования переменных в C # использует
camelCase
кожух вместоsnake_case
кожух.вместо
if
внутриelse
блок, вы должны использоватьelse if
вместо.Что меня беспокоит больше, так это то, что происходит, если в
foundTimes
который удовлетворяет обоим условиям?
Внедрение указанных изменений без учета последнего пункта выглядит так:
private List<DateSharedWork> CalculateDateSharedWork(DateTime startDate, DateTime endDate, ICollection<WorkTime> listWorkTime)
{
List<DateSharedWork> listDateSharedWork = new List<DateSharedWork>();
// +1 to include last day at full
int range = endDate.Subtract(startDate).Days + 1;
// start at startDate
Parallel.For(0, range, i =>
{
DateTime currentDate = startDate.AddDays(i);
//set minute interval
double everyNMinutes = 1.0;
double minutesADay = 1440.0;
// reset counter
int workCounter = 0;
int lowWorkCounter = 0;
int noWorkCounter = 0;
int l = (int)(minutesADay / everyNMinutes);
for (int j = 0; j < l; j++)
{
currentDate = currentDate.AddMinutes(everyNMinutes);
// check if listWorkTime includes current date
var foundTimes = listWorkTime.Where(x => currentDate >= x.FromDate && currentDate <= x.ToDate).ToList();
if (foundTimes.Any(x => x.TimeRangeId == 1))
{
// found interval that is within work hours
workCounter++;
noWorkCounter++;
}
else if (foundTimes.Any(x => x.TimeRangeId == 2))
{
// found intervall that is within low work hours
lowWorkCounter++;
noWorkCounter++;
}
};
double work = everyNMinutes / minutesADay * workCounter;
double lowWork = everyNMinutes / minutesADay * lowWorkCounter;
double noWork = 1.0 - (everyNMinutes / minutesADay * noWorkCounter);
listDateSharedWork.Add(new DateSharedWork(currentDate, work, lowWork, noWork));
});
listDateSharedWork.Sort((x, y) => DateTime.Compare(x.Date, y.Date));
return listDateSharedWork;
}