2

多数の Linq クエリを使用してメソッドを最適化しています。これまでのところ、実行時間は約 3 秒であり、それを短縮しようとしています。このメソッドでは非常に多くの操作と計算が行われますが、それほど複雑ではありません。

パフォーマンスを改善し、コードを最適化する方法について、提案やアイデアをいただければ幸いです。


メソッドのコード全体 (以下に、最大の遅延がある場所を示します):

public ActionResult DataRead([DataSourceRequest] DataSourceRequest request)
{
    CTX.Configuration.AutoDetectChangesEnabled = false;

    var repoKomfortaktion = new KomfortaktionRepository();
    var komfortaktionen = CTX.Komfortaktionen.ToList();

    var result = new List<AqGeplantViewModel>();

    var gruppen = new HashSet<Guid?>(komfortaktionen.Select(c => c.KomfortaktionsGruppeId).ToList());

    var hochgeladeneKomplettabzuege = CTX.Komplettabzug.Where(c => gruppen.Contains(c.KomfortaktionsGruppeId)).GroupBy(c => new { c.BetriebId, c.KomfortaktionsGruppeId }).Select(x => new { data = x.Key }).ToList();
    var teilnehmendeBetriebe = repoKomfortaktion.GetTeilnehmendeBetriebe(CTX, gruppen);
    var hochgeladeneSperrlistenPlz = CTX.SperrlistePlz.Where(c => gruppen.Contains(c.KomfortaktionsGruppeId) && c.AktionsKuerzel != null)
                                                                        .GroupBy(c => new { c.AktionsKuerzel, c.BetriebId, c.KomfortaktionsGruppeId }).Select(x => new { data = x.Key }).ToList();

    var hochgeladeneSperrlistenKdnr = CTX.SperrlisteKdnr.Where(c => gruppen.Contains(c.KomfortaktionsGruppeId) && c.AktionsKuerzel != null)
                                                                        .GroupBy(c => new { c.AktionsKuerzel, c.BetriebId, c.KomfortaktionsGruppeId }).Select(x => new { data = x.Key }).ToList();

    var konfigsProAktion = CTX.Order.GroupBy(c => new { c.Vfnr, c.AktionsId }).Select(c => new { count = c.Count(), c.Key.AktionsId, data = c.Key }).ToList();
    foreach (var komfortaktion in komfortaktionen)
    {
        var item = new AqGeplantViewModel();

        var zentraleTeilnehmer = teilnehmendeBetriebe.Where(c => c.TeilnahmeStatus.Any(x => x.KomfortaktionId == komfortaktion.Id && x.AktionsTypeId == 1)).ToList();
        var lokaleTeilnehmer = teilnehmendeBetriebe.Where(c => c.TeilnahmeStatus.Any(x => x.KomfortaktionId == komfortaktion.Id && x.AktionsTypeId == 2)).ToList();

        var hochgeladeneSperrlistenGesamt =
                hochgeladeneSperrlistenPlz.Count(c => c.data.AktionsKuerzel == komfortaktion.Kuerzel && c.data.KomfortaktionsGruppeId == komfortaktion.KomfortaktionsGruppeId) +
                hochgeladeneSperrlistenKdnr.Count(c => c.data.AktionsKuerzel == komfortaktion.Kuerzel && c.data.KomfortaktionsGruppeId == komfortaktion.KomfortaktionsGruppeId);

        item.KomfortaktionId = komfortaktion.KomfortaktionId;
        item.KomfortaktionName = komfortaktion.Aktionsname;
        item.Start = komfortaktion.KomfortaktionsGruppe.StartAdressQualifizierung.HasValue ? komfortaktion.KomfortaktionsGruppe.StartAdressQualifizierung.Value.ToString("dd.MM.yyyy") : string.Empty;
        item.LokalAngemeldet = lokaleTeilnehmer.Count();
        item.ZentralAngemeldet = zentraleTeilnehmer.Count();

        var anzHochgelandenerKomplettabzuege = hochgeladeneKomplettabzuege.Count(c => zentraleTeilnehmer.Count(x => x.BetriebId == c.data.BetriebId) == 1) +
                                               hochgeladeneKomplettabzuege.Count(c => lokaleTeilnehmer.Count(x => x.BetriebId == c.data.BetriebId) == 1);

        item.KomplettabzugOffen = (zentraleTeilnehmer.Count() + lokaleTeilnehmer.Count()) - anzHochgelandenerKomplettabzuege;

        item.SperrlisteOffen = (zentraleTeilnehmer.Count() + lokaleTeilnehmer.Count()) - hochgeladeneSperrlistenGesamt;

        item.KonfigurationOffen = zentraleTeilnehmer.Count() - konfigsProAktion.Count(c => c.AktionsId == komfortaktion.KomfortaktionId && zentraleTeilnehmer.Any(x => x.Betrieb.Vfnr == c.data.Vfnr));

        item.KomfortaktionsGruppeId = komfortaktion.KomfortaktionsGruppeId;
        result.Add(item);
    }

    return Json(result.ToDataSourceResult(request));
}

前半 (foreach の前) は 0.5 秒かかりますが、問題ありません。最大の遅延は、最初の繰り返しの foreach ステートメント内であり、特にこれらの行では、zentraleTeilnehmer の実行に初めて 1.5 秒かかります。

var zentraleTeilnehmer = teilnehmendeBetriebe.Where(c => c.TeilnahmeStatus.Any(x => x.KomfortaktionId == komfortaktion.Id && x.AktionsTypeId == 1)).ToList();
var lokaleTeilnehmer = teilnehmendeBetriebe.Where(c => c.TeilnahmeStatus.Any(x => x.KomfortaktionId == komfortaktion.Id && x.AktionsTypeId == 2)).ToList();

TeilnehmendeBetriebe には 800 を超える行があり、TeilnahmeStatus プロパティには通常約 4 つの項目があります。つまり、最大 800*4 回の繰り返しですが、これは膨大な数ではありません...

したがって、実行時間を 0.5 秒程度に短縮したいと考えて、これらの行を最適化することにほとんど関心があります。

私が試したこと:

  1. Linq を foreach に書き直します: 同時に役に立ちませんでした...おそらく驚くべきことではありませんが、試してみる価値はありました。

    foreach (var tb in teilnehmendeBetriebe) //836 items
    {
        foreach (var ts in tb.TeilnahmeStatus) //3377 items
        {
            if (ts.KomfortaktionId == komfortaktion.Id && ts.AktionsTypeId == 1)
            {
                testResult.Add(tb);
                break;
            }
        }
    }
    
  2. teilnehmendeBetriebe の特定の列を .Select() で選択する。どちらも助けにはなりませんでした。

私が試した他の小さな操作も役に立ちませんでした。

興味深いことに、foreach の最初の繰り返しには最大 2 秒かかりますが、2 回目以降はミリ秒しかかからないため、.net は計算データを最適化または再利用できます。

パフォーマンスを向上させるために何を変更できるかについてのアドバイスは大歓迎です!

編集:
TeilnahmeBetriebKomfortaktion.TeilnahmeStatusメソッドに積極的にロードされますGetTeilnehmendeBetriebe

    public List<TeilnahmeBetriebKomfortaktion> GetTeilnehmendeBetriebe(Connection ctx, HashSet<Guid?> gruppen)
    {
        return ctx.TeilnahmeBetriebKomfortaktion.Include(
                                        c => c.TeilnahmeStatus).ToList();
    }

Edit2: GetTeilnehmendeBetriebe の実行時に送信されるクエリ:

    SELECT 
    [Extent1].[Id] AS [Id], 
    [Extent1].[BetriebId] AS [BetriebId], 
    [Extent1].[MandantenId] AS [MandantenId], 
    [Extent1].[CreatedUser] AS [CreatedUser], 
    [Extent1].[UpdatedUser] AS [UpdatedUser], 
    [Extent1].[CreatedDate] AS [CreatedDate], 
    [Extent1].[UpdatedDate] AS [UpdatedDate], 
    [Extent1].[IsDeleted] AS [IsDeleted]
    FROM [Semas].[TeilnahmeBetriebKomfortaktion] AS [Extent1]
    WHERE [Extent1].[IsDeleted] <> cast(1 as bit)
4

1 に答える 1

2

私の仮定は、それTeilnahmeBetriebKomfortaktion.TeilnahmeStatusが遅延ロードされたコレクションであり、N + 1の問題が発生することです。パフォーマンスを向上させるために、そのコレクションを積極的に取得する必要があります。

foreach ループの次の反復は高速です。これは、最初の反復の後、これらのオブジェクトはデータベース サーバーから要求されなくなり、メモリからサーバーになるためです。

于 2012-12-14T12:09:22.433 に答える