Быстрый поиск результатов в перечисляемом объекте

Я пытаюсь написать утилиту, чтобы увидеть, если пользователь вошел в 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];

    // ...
}
Другие вопросы по тегам