Быстрый поиск результатов в перечисляемом объекте
Я пытаюсь написать утилиту, чтобы увидеть, если пользователь вошел в Windows с даты, которую я сохранил в базе данных.
private void bwFindDates_DoWork(object sender, DoWorkEventArgs e)
{
UserPrincipal u = new UserPrincipal(context);
u.SamAccountName = "WebLogin*";
PrincipalSearcher ps = new PrincipalSearcher(u);
var result = ps.FindAll();
foreach (WebAccess.WebLoginUsersRow usr in webAccess.WebLoginUsers)
{
UserPrincipal b = (UserPrincipal)result.
Single((a) => a.SamAccountName == usr.WEBUSER);
if (b.LastLogon.HasValue)
{
if (b.LastLogon.Value < usr.MODIFYDATE)
usr.LastLogin = "Never";
else
usr.LastLogin = b.LastLogon.Value.ToShortDateString();
}
else
{
usr.LastLogin = "Never";
}
}
}
Однако производительность очень низкая. Список пользователей, из которого я берусь, насчитывает около 150 пользователей Windows, поэтому, когда я попал в очередь UserPrincipal b = (UserPrincipal)result.Single((a) => a.SamAccountName == usr.CONVUSER);
это занимает от 10 до 15 секунд для его завершения на пользователя (пошагово я вижу, что это делает шаг a.SamAccountName == usr.CONVUSE
запускается для каждого человека, поэтому наихудший случай выполняется O(n^2) раз)
Любые рекомендации о том, как улучшить мою эффективность?
3 ответа
Удивительно, что Single()
должно занять довольно много времени в таком маленьком списке. Я должен верить, что здесь происходит что-то еще. Призыв к ps.FindAll()
может возвращать объект, который не кэширует его результаты, и вынуждает вас делать дорогой вызов некоторого ресурса на каждой итерации в пределах Single()
,
Вы можете использовать профилировщик, чтобы выяснить, куда уходит время, когда вы попадаете на эту строку. Я бы также предложил посмотреть на реализацию FIndAll()
потому что он возвращает что-то необычно дорогое, чтобы перебирать.
Так что после прочтения вашего кода немного ближе, имеет смысл, почему Single()
это так дорого PrincipalSearcher
Класс использует хранилище служб каталогов в качестве хранилища для поиска. Это не кеширует эти результаты. Это то, что влияет на вашу производительность.
Вы, вероятно, хотите материализовать список, используя либо ToList()
или же ToDictionary()
так что доступ к основной информации происходит локально.
Вы также можете полностью избежать такого рода кода и использовать FindOne()
вместо этого метод, который позволяет вам запрашивать непосредственно для основного субъекта, который вы хотите.
Но если вы не можете использовать это, то что-то вроде этого должно работать лучше:
result.ToDictionary(u => u.SamAccountName)[usr.WEBUSER]
Я бы предложил:
var result = ps.FindAll().ToList();
Поскольку PrincipalSearchResult не кешируется, как другие вещи, это приведет вас к уровню производительности O(n).
var userMap = result.ToDictionary(u => u.SamAccountName);
foreach (WebAccess.WebLoginUsersRow usr in webAccess.WebLoginUsers)
{
UserPrincipal b = userMap[usr.WEBUSER];
// ...
}