Jó napot, jó kódolást!
Rég nem volt idő arra, hogy kézbe vevén sorsunk alakulását a hatékony kódolásról írjak.
S hogy az idő hogyan repül, mi sem mutatja jobban, hogy míg szeretett Inveniónk keresőmotorjának alapjai felépülnek egy-egy telepítés után, arcunkat megcserzi LCD kijelzőnk gyengécske fénye.
Nekiláttam hát megnézni, hogyan repülhet el több perc, míg a „search schema generálódik”.
Az első profile mérést követően ilyesmi fogadott:
A képről jól látható, hogy a legforróbb (jujjdejó! J) kód a MetadataTools.GetAclIdFieldsForCacheTable() metódus hívása, rögtön az első két leglassabb kód dobogós helyezését megkapta.
További nézelődés után kiderült, hogy az említett metódus belseje 9000-nél is többször hívódik, ami tekintve az adatbázis táblák számát azt jelenti, hogy 1-1 táblát legalább 10-szer újra processzálunk.
Hogyan is szól az ismerős mantra? Cachelni, cachelni, khm... CACHELNI. Lássuk, hol lehet gyorsítani az eljárásunkon!
Az eredeti kód ez volt:
IEnumerable<TypeContainer> containers = DALProvider.Metadata.TypeContainers.Where(
t => t.IsMapped && !t.IsInfoContainer && !t.IsInfoBaseContainer && t.TableMapping.SchemaName == baseTableSchema && t.TableMapping.TableName == baseTableName);
if (containers.Count() > 0)
{
container = containers.First();
if (container.SecurityDomains != null)
{
foreach (var item in container.SecurityDomains.Values)
{
if (item is AuthorizationSecuredObjectTypeAttribute)
{
var securedObjectType = (AuthorizationSecuredObjectTypeAttribute)item;
var aclField = new Field(securedObjectType.AclColumnName, Service.GetAclcolumnType, true);
aclIdFields.Add(aclField);
}
}
}
}
Nosza, ásó-kapa-refaktor gomb, a csúnya lookup részt imígyen faragtam élesebbre egy meglévő, cache-elt lookup-oláshoz készült metódus segítségével:
var aclIdFields2 = new List<Field>();
TypeContainer container2 = null;
if (DALProvider.Metadata.TryGetMappedContainer(baseTableSchema, baseTableName, out container2))
{
if (container2.SecurityDomains != null)
{
foreach (var item in container2.SecurityDomains.Values)
{
if (item is AuthorizationSecuredObjectTypeAttribute)
{
var securedObjectType = (AuthorizationSecuredObjectTypeAttribute)item;
var aclField = new Field(securedObjectType.AclColumnName, Service.GetAclcolumnType, true);
aclIdFields2.Add(aclField);
}
}
}
}
Hogy validáljam az egyszerűsítést, mindkét kódblokkot meghagytam egy mérés erejéig, diff-et készítettem a két megoldásról az ellenőrzés végett, és Stopwatch-al kimértem az egyes futási időket.
Ilyen lett a végső metódus:
internal static List<Field> GetAclIdFieldsForCacheTable(string baseTableSchema, string baseTableName)
{
var aclIdFields = new List<Field>();
InitializeDalProvider();
TypeContainer container = null;
//DALProvider.Metadata.TryGetMappedContainer(baseTableSchema, baseTableName, out container);
Stopwatch sw1 = Stopwatch.StartNew();
// DEVNOTE: Do NOT do reverse-lookups like below, there are cached lookup-methods available!
var containers = DALProvider.Metadata.TypeContainers.Where(t => t.IsMapped && !t.IsInfoContainer && !t.IsInfoBaseContainer && t.TableMapping.SchemaName == baseTableSchema && t.TableMapping.TableName == baseTableName);
if (containers.Count() > 0)
{
container = containers.First();
if (container.SecurityDomains != null)
{
foreach (var item in container.SecurityDomains.Values)
{
if (item is AuthorizationSecuredObjectTypeAttribute)
{
var securedObjectType = (AuthorizationSecuredObjectTypeAttribute)item;
var aclField = new Field(securedObjectType.AclColumnName, Service.GetAclcolumnType, true);
aclIdFields.Add(aclField);
}
}
}
}
sw1.Stop();
var sw2 = Stopwatch.StartNew();
var aclIdFields2 = new List<Field>();
var container2 = null;
if (DALProvider.Metadata.TryGetMappedContainer(baseTableSchema, baseTableName, out container2))
{
if (container2.SecurityDomains != null)
{
foreach (var item in container2.SecurityDomains.Values)
{
if (item is AuthorizationSecuredObjectTypeAttribute)
{
var securedObjectType = (AuthorizationSecuredObjectTypeAttribute)item;
var aclField = new Field(securedObjectType.AclColumnName, Service.GetAclcolumnType, true);
aclIdFields2.Add(aclField);
}
}
}
}
sw2.Stop();
_log.Info("Original lookup took " + sw1.Elapsed.TotalMilliseconds + " ms (" + sw1.Elapsed.Ticks + " ticks), optimized lookup took " + sw2.Elapsed.TotalMilliseconds + " ms (" + sw2.Elapsed.Ticks + " ticks), difference was " + ((100 * ((double)sw1.Elapsed.Ticks / (double)sw2.Elapsed.Ticks)) - 100).ToString() + "%");
Differencer.AddKeyAccessor(typeof(Field), new Getter(f => ((Field)f).Name), false);
Diff diff = Differencer.Compare(aclIdFields, aclIdFields2);
if (diff.Type != DiffType.Unchanged)
{
_log.Error("Second lookup of [" + baseTableSchema + "].[" + baseTableName + "] resulted in different AclID field list!");
}
return aclIdFields;
}
Ha a százalékszámítást valaki szerint rosszul csináltam, szóljatok rám, azért álljon itt példának okáért néhány bejegyzés a logból:
2010-11-05 16:21:24,771 [6] INFO MetadataManager [SearchConfigurationService.CreateSearchStructures CreateMetadataObjects CreateCacheTables] - Original lookup took 4,2451 ms (42451 ticks), optimized lookup took 0,7844 ms (7844 ticks), difference was 441,190719020908%
2010-11-05 16:21:24,774 [6] INFO MetadataManager [SearchConfigurationService.CreateSearchStructures CreateMetadataObjects CreateCacheTables] - Original lookup took 0,4821 ms (4821 ticks), optimized lookup took 0,0101 ms (101 ticks), difference was 4673,26732673267%
2010-11-05 16:21:24,775 [6] INFO MetadataManager [SearchConfigurationService.CreateSearchStructures CreateMetadataObjects CreateCacheTables] - Original lookup took 0,5033 ms (5033 ticks), optimized lookup took 0,0074 ms (74 ticks), difference was 6701,35135135135%
2010-11-05 16:21:24,776 [6] INFO MetadataManager [SearchConfigurationService.CreateSearchStructures CreateMetadataObjects CreateCacheTables] - Original lookup took 0,5063 ms (5063 ticks), optimized lookup took 0,0039 ms (39 ticks), difference was 12882,0512820513%
2010-11-05 16:21:24,777 [6] INFO MetadataManager [SearchConfigurationService.CreateSearchStructures CreateMetadataObjects CreateCacheTables] - Original lookup took 0,509 ms (5090 ticks), optimized lookup took 0,0039 ms (39 ticks), difference was 12951,2820512821%
2010-11-05 16:21:24,778 [6] INFO MetadataManager [SearchConfigurationService.CreateSearchStructures CreateMetadataObjects CreateCacheTables] - Original lookup took 0,5591 ms (5591 ticks), optimized lookup took 0,0039 ms (39 ticks), difference was 14235,8974358974%
2010-11-05 16:21:24,779 [6] INFO MetadataManager [SearchConfigurationService.CreateSearchStructures CreateMetadataObjects CreateCacheTables] - Original lookup took 0,4016 ms (4016 ticks), optimized lookup took 0,0039 ms (39 ticks), difference was 10197,4358974359%
2010-11-05 16:21:24,780 [6] INFO MetadataManager [SearchConfigurationService.CreateSearchStructures CreateMetadataObjects CreateCacheTables] - Original lookup took 0,5803 ms (5803 ticks), optimized lookup took 0,0039 ms (39 ticks), difference was 14779,4871794872%
2010-11-05 16:21:24,781 [6] INFO MetadataManager [SearchConfigurationService.CreateSearchStructures CreateMetadataObjects CreateCacheTables] - Original lookup took 0,5314 ms (5314 ticks), optimized lookup took 0,0039 ms (39 ticks), difference was 13525,641025641%
2010-11-05 16:21:24,783 [6] INFO MetadataManager [SearchConfigurationService.CreateSearchStructures CreateMetadataObjects CreateCacheTables] - Original lookup took 0,5904 ms (5904 ticks), optimized lookup took 0,0096 ms (96 ticks), difference was 6050%
2010-11-05 16:21:24,783 [6] INFO MetadataManager [SearchConfigurationService.CreateSearchStructures CreateMetadataObjects CreateCacheTables] - Original lookup took 0,5349 ms (5349 ticks), optimized lookup took 0,0035 ms (35 ticks), difference was 15182,8571428571%
2010-11-05 16:21:24,795 [6] INFO MetadataManager [SearchConfigurationService.CreateSearchStructures CreateMetadataObjects CreateCacheTables] - Original lookup took 0,4725 ms (4725 ticks), optimized lookup took 0,0052 ms (52 ticks), difference was 8986,53846153846%
Nos igen.
Ha egy-egy lookup ~4400 helyett 40 tick, az annyi mint... 100-szoros gyorsulás, köbö 1000%. Plusz-mínusz néhány száz %, ahogy elnézem.
Tanulság
Röviden a lényeg:
· A cache-elt lookup-olás, főleg gyakran használt ám sosem/ritkán változó elemeknél a 21. században nem opció – kötelező!
· A LINQ query támogatás jó, jó... de nem mindenre.
· Egymás kódjába belesz@rni nem szép dolog... kivéve, ha 100%-nál nagyobb gyorsulást eredményez változatlan működés mellett.