Як я магу зрабіць гэты код хутчэй ?? Спіс <custom_class> (). Find (lambda_expression)

Мне трэба, каб адпавядаць па электроннай пошце адпраўляецца з электроннымі адскок, так што я магу знайсці, калі яны былі дастаўлены ці не. Загвоздка ў тым, што я павінен абмежаваць адмовы на працягу 4 дзён пасля адпраўкі, каб ліквідаваць адпаведныя няправільны пасыл на рыкашэт. Адправіць запісу распаўсюджваюцца на працягу 30 дзён.

LinkedList sent = GetMyHugeListOfSends(); //for example 1M+ records
List bounced = GetMyListOfBounces(); //for example 150k records

bounced = bounced.OrderBy(o => o.event_date).ToList(); //this ensures the most accurate match of bounce to send (since we find the first match)

List delivered = new List();
event_data deliveredEmail = new event_data();

foreach (event_data sentEmail in sent)
{

     event_data bounce = bounced.Find(item => item.email.ToLower() == sentEmail.email.ToLower() && (item.event_date > sentEmail.event_date && item.event_date < sentEmail.event_date.AddDays(deliveredCalcDelayDays)));

     //create delivered records
     if (bounce != null)
     {
          //there was a bounce! don't add a delivered record!
     }
     else
     {
          //if sent is not bounced, it's delivered
          deliveredEmail.sid = siteid;
          deliveredEmail.mlid = mlid;
          deliveredEmail.mid = mid;
          deliveredEmail.email = sentEmail.email;
          deliveredEmail.event_date = sentEmail.event_date;
          deliveredEmail.event_status = "Delivered";
          deliveredEmail.event_type = "Delivered";
          deliveredEmail.id = sentEmail.id;
          deliveredEmail.number = sentEmail.number;
          deliveredEmail.laststoretransaction = sentEmail.laststoretransaction;

          delivered.Add(deliveredEmail);   //add the new delivered
          deliveredEmail = new event_data();

          //remove bounce, it only applies to one send!
          bounced.Remove(bounce);
     }

     if (bounced.Count() == 0)
     {
          break; //no more bounces to match!
     }
}

Так што я зрабіў некаторыя выпрабаванні, і гэта апрацоўка каля 12 адпраўленых запісаў у секунду. В + запісаў 1M, гэта зойме 25+ гадзін, каб апрацаваць!

Два пытанні:

  1. Як я магу знайсці дакладную лінію, якая займае больш за ўсё часу?
  2. Я мяркую, што гэта лямбда-выраз знаходжання адскоку, якая займае самы доўгі, так як гэта было нашмат хутчэй, перш чым я паклаў, што там. Як я магу паскорыць гэта?

Дзякуй!

<�Моцны> Edit

--- Ідэі ---

  1. Адна ідэя, якую я толькі што для сартавання пасылаў па даце, як я зрабіў падскоквае так, што пошук праз адскокам будзе больш эфектыўным, так як рана пасылу будзе, хутчэй за ўсё, трапіў ранні адскок, а таксама.
  2. Яшчэ адна ідэя, якую я толькі што гэта запусціць пару гэтых працэсаў паралельна, хоць я не хацеў бы, каб шматструменнай гэта простае дадатак.
2
Яшчэ адна рэч, прадукцыйнасць, але не дастаткова для адказу. замест item.email.ToLower() == sentEmail.email.ToLower() зрабіць item.email.Equals (sentEmail.email, StringComparison.OrdinalIgnoreCase) ён трымае вас ад таго, каб генераваць два новы радок, кожны раз, калі для параўнання двух элементаў.
дададзена аўтар Scott Chamberlain, крыніца
Дзякуючы Быдла, я буду выкарыстоўваць гэта таксама. Мне трэба Іч кожную апошнюю кроплю прадукцыйнасці, што я магу. :-)
дададзена аўтар richard, крыніца
Прывітанне Jodrell. Там можа быць некалькі адскокам ў дыяпазоне 4 дні. Парадак проста робіць, што я атрымліваю першы адпаведны адскок (які з'яўляецца тое, што я хачу).
дададзена аўтар richard, крыніца
Я перапрацаваны мой адказ, каб адпавядаць вашым сённяшнім патрабаванням, stackoverflow.com/a/16709825/659190
дададзена аўтар Jodrell, крыніца
Я быў дурны, я прапусціў выдаліць, пераробку майго адказу.
дададзена аўтар Jodrell, крыніца
@RichardDesLonde пасля чытання вашага пытання, я цікава, чаму парадак адскокам адносіны? Усё, што вы тэстуеце для з'яўляецца наяўнасць адмоваў у адносным дыяпазоне дат, вы не выкарыстоўваеце дадзеныя з рыкашэту дык чаму заказ?
дададзена аўтар Jodrell, крыніца

7 адказы

Я б разумна ўпэўнена сказаць, што так, гэта ваша знаходка, якая займае час.

It looks like you are certain that the find method will return 0 or 1 records only (not a list) in which case the way to speed this up would be to create a lookup (a dictionary) instead of creating a List for your bounced var, create a Dictionary instead, then you can just look-up the value by key instead of doing a find.

Хітрасць заключаецца ў тым, у стварэнні ключа (я не ведаю дастаткова аб вашым дадатку, каб дапамагчы з гэтым), але па сутнасьці аднымі і тымі ж крытэрамі, што знаходзіцца ў вашай знаходцы.

EDIT. (Дадаўшы некаторыя псеўда-код)

void Main()
{
    var hugeListOfEmails = GetHugeListOfEmails();
    var allBouncedEmails = GetAllBouncedEmails();
    IDictionary CreateLookupOfBouncedEmails = CreateLookupOfBouncedEmails(allBouncedEmails);

    foreach(var info in hugeListOfEmails)
    {
        if(CreateLookupOfBouncedEmails.ContainsKey(info.emailAddress))
        {
           //Email is bounced;
        }
        else
        {
           //Email is not bounced
        }
    }

}

public IEnumerable GetHugeListOfEmails()
{
    yield break;
}

public IEnumerable GetAllBouncedEmails()
{
    yield break;
}

public IDictionary CreateLookupOfBouncedEmails(IEnumerable emailList)
{
    var result = new Dictionary();
    foreach(var e in emailList)
    {
        if(!result.ContainsKey(e.emailAddress))
        {
            if(//satisfies the date conditions)
            {
                result.Add(e.emailAddress, e);
            }
        }
    }
    return result;
}

public class EmailInfo
{
    public string emailAddress { get; set; }
    public DateTime DateSent { get; set; }
}
4
дададзена
Цім, што вы думаеце аб выкарыстанні картэжа для гэтага?
дададзена аўтар richard, крыніца
Тое ж самае з пасылае ... там можа быць некалькі адпраўляе на той жа адрас электроннай пошты з рознымі датамі пасылу.
дададзена аўтар richard, крыніца
Такім чынам, праблема заключаецца ў тым, што электронная пошта не з'яўляецца унікальным ... там можа быць некалькі адмоваў запісу аднаго і таго ж адрасу электроннай пошты ... кожны з рознай датай адскоку.
дададзена аўтар richard, крыніца
Так, вы маеце рацыю, на самай справе ... асабліва калі я раблю гэта спарадкаваны слоўнік ...
дададзена аўтар richard, крыніца
Прывітанне Цім - Ён павінен знайсці адпаведны адрас электроннай пошты, і першы моцны ўдар на працягу 4 дзён пасля адпраўкі ... як бы я стварыць ключ, як гэта? Адрас электроннай пошты і дата адмоваў?
дададзена аўтар richard, крыніца
На самай справе прыгледзеўшыся на свой код, ключ будзе проста адрас электроннай пошты. проста фільтраваць па вашых крытэрах ў пабудове слоўніка.
дададзена аўтар Tim Jarvis, крыніца
Так, вы можаце выкарыстоўваць Картэж з адрасам электроннай пошты і датай. Аднак, калі б гэта было, я б проста запоўніць слоўнік з вернутымі лістамі ў межах дыяпазону дат, якія вы хочаце праверыць, так што адрас электроннай пошты з'яўляецца ўнікальнай.
дададзена аўтар Tim Jarvis, крыніца

Вы павінны палепшыць з дапамогай ToLookup метад для стварэння табліцы пошуку па адрасе электроннай пошты

var bouncedLookup = bounced.ToLookup(k => k.email.ToLower());

і выкарыстоўваць гэта ў цыкле для пошуку па электроннай пошце першага

var filteredBounced = bouncedLookup[sent_email.email.ToLower()];
// mini optimisation here
var endDate = sentEmail.event_date.AddDays(deliveredCalcDelayDays);
event_data bounce = filteredBounced.Find(item => item.event_date > sentEmail.event_date && item.event_date < endDate));

Я не мог сабраць яго, але я думаю, што трэба рабіць. Калі ласка, паспрабуйце.

1
дададзена
Дзякуй! Я буду разглядаць гэтую прапанову, а таксама.
дададзена аўтар richard, крыніца

Ok канчатковае рашэнне, якое я знайшоў быў слоўнік для адскоку.

Адпраўлена LinkedList было Сартаваць па sent_date так, што гэта будзе цыкл праз ў храналагічным парадку. Гэта важна, таму што я павінен адпавядаць праву адправіць на правы адскокам.

I made a Dictionary>, so the key was email, and the value was a List of all bounces for the email address. The List was sorted by event_date since I wanted to make sure the first bounce was matched to the send.

<�Моцны> Канчатковы вынік ... ён пайшоў ад апрацоўкі 700 запісаў/мін да 500k + запісу/другі.

Вось канчатковы код:

LinkedList sent = GetMyHugeListOfSends(); IEnumerable sentOrdered = sent.OrderBy(send => send.event_date);

Dictionary> bounced = GetMyListOfBouncesAsDictionary();

Спіс пастаўляецца = новы спіс (); event_data deliveredEmail = новы event_data ();

Спіс адскоквае = NULL; BOOL matchedBounce = хлусня;

Еогеасп (event_data sentEmail ў sentOrdered) {      matchedBounce = FALSE;

 //create delivered records
 if (bounced.TryGetValue(sentEmail.email, out bounces))
 {
      //there was a bounce! find out if it was within 4 days after the send!
      foreach (event_data bounce in bounces)
      {
           if (bounce.event_date > sentEmail.event_date &&
               bounce.event_date <= sentEmail.event_date.AddDays(4))
           {
               matchedBounce = true;

               //remove the record because a bounce can only match once back to a send
               bounces.Remove(bounce);

               if(bounces.Count == 0) //no more bounces for this email
               {
                    bounced.Remove(sentEmail.email);
               }

               break;
          }
     }

     if (matchedBounce == false) //no matching bounces in the list!
     {
          //if sent is not bounced, it's delivered
          deliveredEmail.sid = siteid;
          deliveredEmail.mlid = mlid;
          deliveredEmail.mid = mid;
          deliveredEmail.email = sentEmail.email;
          deliveredEmail.event_date = sentEmail.event_date;
          deliveredEmail.event_status = "Delivered";
          deliveredEmail.event_type = "Delivered";
          deliveredEmail.id = sentEmail.id;
          deliveredEmail.number = sentEmail.number;
          deliveredEmail.laststoretransaction = sentEmail.laststoretransaction;

          delivered.Add(deliveredEmail);   //add the new delivered
          deliveredEmail = new event_data();
     }
 }
 else
 {
      //if sent is not bounced, it's delivered
      deliveredEmail.sid = siteid;
      deliveredEmail.mlid = mlid;
      deliveredEmail.mid = mid;
      deliveredEmail.email = sentEmail.email;
      deliveredEmail.event_date = sentEmail.event_date;
      deliveredEmail.event_status = "Delivered";
      deliveredEmail.event_type = "Delivered";
      deliveredEmail.id = sentEmail.id;
      deliveredEmail.number = sentEmail.number;
      deliveredEmail.laststoretransaction = sentEmail.laststoretransaction;

      delivered.Add(deliveredEmail);   //add the new delivered
      deliveredEmail = new event_data();
 }

 if (bounced.Count() == 0)
 {
      break; //no more bounces to match!
 }
}
0
дададзена
фарматаванне выключана.
дададзена аўтар Jodrell, крыніца

Аб разглядзе, лік адскокам з'яўляецца адносна невялікім, такім чынам,

Чаму загадзя ня omptimise адскок пошук настолькі, наколькі гэта магчыма, гэты код робіць дэлегат для кожнага магчымага адскоку і групуюць іх у слоўнік для доступу да ключа электроннай пошты.

private static DateInRange(
    DateTime sendDate,
    DateTime bouncedDate,
    int deliveredCalcDelayDays)
{
    if (sentDate < bouncedDate)
    {
        return false;
    }

    return sentDate < bouncedDate.AddDays(deliveredCalcDelayDays);
}

static IEnumerable GetDeliveredMails(
           IEnumerable sent,
           IEnumerable bounced,
           int siteId,
           int mlId,
           int mId,
           int deliveredCalcDelayDays)
{
    var grouped = bounced.GroupBy(
        b => b.email.ToLowerInvariant());

    var lookup = grouped.ToDictionary(
        g => g.Key,
        g => g.OrderBy(e => e.event_date).Select(
            e => new Func(
                s => DateInRange(s, e.event_date, deliveredCalcDelayDays))).ToList());

    foreach (var s in sent)
    {
        var key = s.email.ToLowerInvariant();

        List> checks;
        if (lookup.TryGetValue(key, out checks))
        {
            var match = checks.FirstOrDefault(c => c(s.event_date));
            if (match != null)
            {
                checks.Remove(match);
                continue;
            }
        }

        yield return new event_data
            {
                .sid = siteid;
                .mlid = mlid;
                .mid = mid;
                .email = s.email;
                .event_date = s.event_date;
                .event_status = "Delivered";
                .event_type = "Delivered";
                .id = s.id;
                .number = s.number;
                .laststoretransaction = s.laststoretransaction
            };
    }

}

Вы можаце паспрабаваць папярэдне кампіляцыі дэлегатаў у пошуку, калі гэта не дастаткова хутка.

0
дададзена

Вы знайсці элементы ў спісе. Гэта азначае, што ён павінен прайсці ўвесь спіс, так што парадак (п) аперацыі. Не маглі б вы не захоўваць гэтыя адпраўленыя паведамленні электроннай пошты ў слоўніку з ключом з'яўляецца адрасам электроннай пошты, вы шукаеце на. Прайсці праз адскокам б спасылаліся назад у электронную пошту ў слоўніку. Пошук будзе пастаянная час і вы будзеце ісці праз адскоку так будзе парадку (п) у цэлым. Вы ў цяперашні час метад з'яўляецца парадак (п квадрат)

0
дададзена
Такім чынам, вы рекомендуете рабіць пасланыя дадзеныя слоўніка, і прабягаем па адскокам, каб знайсці карэлюе адправіць для кожнага адскоку?
дададзена аўтар richard, крыніца

Там іншы клопат аб вашым кодзе, што я хачу адзначыць.

Спажыванне памяці. Я не ведаю канфігурацыю машыны, але вось некаторыя думкі пра код:

  1. Першапачаткова вы вылучэнне месца для 1,2m + аб'ектаў event_data </​​код> тыпу. Я не магу бачыць, event_data </​​код> поўнае вызначэнне тыпу, але пры ўмове, што лісты з'яўляюцца унікальнымі і, бачачы, што тып мае даволі шмат ўласцівасці, я магу выказаць здагадку, што такая калекцыя, а <�ет> цяжкі (Сотні Meg магчыма).
  2. Далей вылучаем яшчэ адну кучу event_data </​​код> аб'екты (Амаль 1M, калі я палічыў гэта права). Гэта становіцца нават цяжэйшы з пункту гледжання спажывання памяці
  3. Я не ведаю, аб іншых аб'ектах, якія прысутнічаюць у дадзенай мадэлі вашага прыкладання, але, улічваючы ўсе, што я згадаў, вы можаце лёгка атрымаць блізка да мяжы памяці для 32-бітнага працэсу і тым самым прымусіць GC працаваць <�моцны> вельмі часта </моцны>. На самой справе Вы можаце лёгка мець GC збору пасля кожнага выкліку <�Код> bounced.Remove (ўступаў); І гэта сапраўды будзе <�моцны> значна запаволіць ваша прыкладанне
  4. .

Такім чынам, нават калі ў Вас ёсць вялікая колькасць памяці засталося і/або ваша прыкладанне 64-біт, я хацеў бы паспрабаваць звесці да мінімуму спажыванне памяці. Упэўнены, гэта будзе атрымаць код працаваць хутчэй. Напрыклад, вы можаце зрабіць поўную апрацоўку deliveredEmail , ня захоўваючы яго, або загрузіць свой першапачатковы event_data </​​код> на кавалкі і г.д.

0
дададзена
Прывітанне Koka, дзякуй за параду. На жаль, у мяне няма ніякага іншага спосабу зрабіць гэта (што я магу думаць). Я атрымліваю дадзеныя ад выкліку API і павінен трымаць яго ў памяці для таго, каб разлічыць дастаўленыя дадзеныя.
дададзена аўтар richard, крыніца

Пераўтварэнне адкінутым ў SortedList можа быць добрым рашэннем

SortedList sl = new SortedList(bounced.ToDictionary(s=>s.email,s=>s));
and to find a bounce use
sl.Select(c=>c.Key.Equals(item => item.email,StringComparison.OrdinalIgnoreCase) && ...).FirstOrDefault();
0
дададзена