इसलिए मैंने एफ # को देने का फैसला किया है और मैंने इसे ए # एल्गोरिदम को पोर्ट किया है जिसे मैंने सी # में लिखा है। एक बिंदु पर, मैंने देखा है कि रिलीज की तुलना में डिबग बिल्ड तेजी से चल रहा है। इसके बाद मैंने ऑप्टिमाइज़ेशन सेटिंग्स के साथ खेला और इन परिणामों को मिला:अनुकूलित कोड से गैर-अनुकूलित एफ # कोड तेज क्या हो सकता है?
बार 100000 रनों से एल्गोरिदम का कुल निष्पादन समय दिखाता है। मैं एफ # कंपाइलर का उपयोग कर रहा हूं जो विजुअल स्टूडियो 2010 एसपी 1 के साथ आता है। लक्ष्य मंच कोई सीपीयू है।
Opt off, tail calls off: 5.81s
Opt off, tail calls on : 5.79s
Opt on , tail calls off: 6.48s
Opt on , tail calls on : 6.40s
मैं वास्तव में इस हैरान हूँ - क्यों अनुकूलन कोड रन गति धीमी हो जाता है? एल्गोरिदम का सी # संस्करण इस व्यवहार को प्रदर्शित नहीं करता है (यह भी थोड़ा अलग तरीके से कार्यान्वित किया जाता है)
यहां एफ # कोड का एक अलग संस्करण है, यह एक एल्गोरिदम है जो अणुओं में पैटर्न पाता है। यह कोड जो इस एफ # प्रोग्राम पर निर्भर करता है वह एफ # में लिखा गया है।
namespace Motives
module Internal =
type Motive =
{ ResidueSet: Set<Residue>; AtomSet: Set<IAtom> }
member this.Atoms : IAtom seq =
seq {
for r in this.ResidueSet do yield! r.Atoms
yield! this.AtomSet
}
static member fromResidues (residues : Residue seq) = residues |> Seq.fold (fun (m: Set<Residue>) r -> m.Add(r)) Set.empty |> fun rs -> { ResidueSet = rs; AtomSet = Set.empty }
static member fromAtoms (atoms : IAtom seq) = atoms |> Seq.fold (fun (m: Set<IAtom>) a -> m.Add(a)) Set.empty |> fun atoms -> { ResidueSet = Set.empty; AtomSet = atoms }
static member merge (m1: Motive) (m2: Motive) = { ResidueSet = Set.union m1.ResidueSet m2.ResidueSet; AtomSet = Set.union m1.AtomSet m2.AtomSet }
static member distance (m1: Motive) (m2: Motive) = Seq.min (seq { for a in m1.Atoms do for b in m2.Atoms -> a.Position.DistanceTo(b.Position) })
type Structure with
static member fromMotive (m: Motive) (parent: IStructure) (addBonds: bool) : IStructure =
let atoms = AtomCollection.FromUniqueAtoms(m.Atoms)
let bonds =
match addBonds with
| true -> BondCollection.Create(atoms |> Seq.map (fun a -> parent.Bonds.[a]) |> Seq.concat)
| _ -> BondCollection.Empty
Structure.Create (parent.Id + "_" + atoms.[0].Id.ToString(), atoms, bonds)
// KDTree used for range queries
// AminoChains used for regex queries
type StructureContext =
{ Structure: IStructure; KDTree: Lazy<KDAtomTree>; AminoChains: Lazy<(Residue array * string) list> }
static member create (structure: IStructure) =
match structure.IsPdbStructure() with
| false -> { Structure = structure; KDTree = Lazy.Create(fun() -> structure.Atoms.ToKDTree()); AminoChains = Lazy.CreateFromValue([]) }
| true ->
let aminoChains = new System.Func<(Residue array * string) list> (fun() ->
let residues = structure.PdbResidues() |> Seq.filter (fun r -> r.IsAmino)
residues
|> Seq.groupBy (fun r -> r.ChainIdentifier)
|> Seq.map (fun (k,rs) -> rs |> Array.ofSeq, String.concat "" (rs |> Seq.map (fun r -> r.ShortName)))
|> List.ofSeq)
{ Structure = structure; KDTree = Lazy.Create(fun() -> structure.Atoms.ToKDTree()); AminoChains = Lazy.Create(aminoChains) }
// Remember the named motives from named patterns
type MatchContext =
{ StructureContext: StructureContext; NamedMotives: Map<string, Motive> }
static member merge (c1: MatchContext) (c2: MatchContext) =
{ StructureContext = c1.StructureContext; NamedMotives = c2.NamedMotives |> Map.fold (fun m k v -> m.Add(k,v)) c1.NamedMotives }
type MatchedMotive = Motive * MatchContext
type Pattern =
| EmptyPattern
| GeneratingPattern of (StructureContext -> MatchedMotive seq)
| ConstraintPattern of (MatchedMotive -> MatchedMotive option) * Pattern
static member matches (p: Pattern) (context: StructureContext) : MatchedMotive seq =
match p with
| GeneratingPattern generator -> generator context
| ConstraintPattern (transform, pattern) ->
Pattern.matches pattern context
|> Seq.choose (fun m -> transform m)
| _ -> Seq.empty
let ringPattern (names: string list) =
let fingerprint =
names
|> Seq.map (fun s -> ElementSymbol.Create(s).ToString())
|> Seq.sort
|> String.concat ""
let generator (context: StructureContext) =
let rings = context.Structure.Rings().GetRingsByFingerprint(fingerprint)
rings |> Seq.map (fun r -> Motive.fromAtoms r.Atoms, { StructureContext = context; NamedMotives = Map.empty })
GeneratingPattern generator
open Internal
type MotiveFinder (pattern: string) =
// I am using a hard coded pattern here for testing purposes
let pattern = ringPattern ["C"; "C"; "C"; "C"; "C"; "O"]
member this.Matches (structure: IStructure) =
Pattern.matches pattern (StructureContext.create structure)
|> Seq.map (fun (m, mc) -> Structure.fromMotive m mc.StructureContext.Structure false)
|> List.ofSeq
|> List.sortBy (fun s -> s.Atoms.[0].Id)
///////////////////////////////////////////////////////////////////
// performance test
let warmUp = (new MotiveFinder("")).Matches (StructureReader.ReadPdb(filename, computeBonds = true))
printfn "%i" (List.length warmUp)
let structure = StructureReader.ReadPdb(filename, computeBonds = true)
let stopWatch = System.Diagnostics.Stopwatch.StartNew()
let nruns = 100000
let result =
seq {
for i in 1 .. nruns do
yield (new MotiveFinder("")).Matches structure
} |> Seq.nth (nruns-1)
stopWatch.Stop()
printfn "Time elapsed: %f seconds" stopWatch.Elapsed.TotalSeconds
EDIT2:
मैं लग सेट प्रकार के कार्यान्वयन के लिए समस्या को संकुचित होता है करने के लिए। पर अनुकूलन के साथ अनुकूलन बंद साथ
let stopWatch = System.Diagnostics.Stopwatch.StartNew()
let runs = 1000000
let result =
seq {
for i in 1 .. runs do
let setA = [ 1 .. (i % 10) + 5 ] |> Set.ofList
let setB = [ 1 .. (i % 10) + 5 ] |> Set.ofList
yield Set.union setA setB
} |> Seq.nth (runs - 1)
stopWatch.Stop()
printfn "Time elapsed: %f seconds" stopWatch.Elapsed.TotalSeconds
printfn "%A" result
मैं ~ 7.5s और ~ 8.0s:
इस कोड के लिए। अभी भी लक्ष्य = कोई भी CPU (और मेरे पास i7-860 प्रोसेसर है)।
EDIT3: और मैंने पिछले संपादन को पोस्ट करने के ठीक बाद मुझे लगा कि मुझे इसे केवल सूचियों पर आज़माएं।
तो के लिए
let stopWatch = System.Diagnostics.Stopwatch.StartNew()
let runs = 1000000
let result1 =
seq {
for i in 1 .. runs do
let list = [ 1 .. i % 100 + 5 ]
yield list
} |> Seq.nth (runs - 1)
stopWatch.Stop()
printfn "Time elapsed: %f seconds" stopWatch.Elapsed.TotalSeconds
printfn "%A" result1
मैं ~ ऑप्ट साथ 3s मिलता है। ऑप्ट के साथ बंद और ~ 3.5s। पर।
EDIT4:
अगर मैं seq बिल्डर को हटा दें और सिर्फ
let stopWatch = System.Diagnostics.Stopwatch.StartNew()
let runs = 1000000
let mutable ret : int list = []
for i in 1 .. runs do
let list = [ 1 .. i % 100 + 5 ]
ret <- list
stopWatch1.Stop()
printfn "Time elapsed: %f seconds" stopWatch.Elapsed.TotalSeconds
printfn "%A" ret
मैं ~ दोनों पर और बंद अनुकूलन के साथ 3s है। तो ऐसा लगता है कि सीईसी बिल्डर कोड को अनुकूलित करने में समस्या कहीं है।
अजीब पर्याप्त, मैं सी # में एक परीक्षण एप्लिकेशन लिखा है:
var watch = Stopwatch.StartNew();
int runs = 1000000;
var result = Enumerable.Range(1, runs)
.Select(i => Microsoft.FSharp.Collections.ListModule.OfSeq(Enumerable.Range(1, i % 100 + 5)))
.ElementAt(runs - 1);
watch.Stop();
Console.WriteLine(result);
Console.WriteLine("Time: {0}s", watch.Elapsed.TotalSeconds);
और कोड पर एफ # समाधान के रूप में लगभग दो बार के रूप में तेजी से चलाने के लिए होता है ~ 1.7s।
EDIT5:
मैं बात यह है कि धीमी चलाने अनुकूलित कोड उत्पन्न कर रहा है पता चला है जॉन हारॉप साथ चर्चा के आधार पर (मैं अभी भी क्यों यद्यपि पता नहीं है)।
दोनों अनुकूलित और गैर-अनुकूलित संस्करण मेंअगर मैं
member this.Atoms : IAtom seq =
Seq.append (this.ResidueSet |> Seq.collect (fun r -> r.Atoms)) this.AtomSet
को
member this.Atoms : IAtom seq =
seq {
for r in this.ResidueSet do yield! r.Atoms
yield! this.AtomSet
}
से Motive.Atoms
बदल तो कार्यक्रम चलाता है ~ 7.1s। जो seq
संस्करण से धीमा है, लेकिन कम से कम संगत।
तो ऐसा लगता है कि एफ # संकलक सिर्फ गणना भाव अनुकूलित नहीं कर सकते और वास्तव में उन्हें इतना कोशिश कर रहा द्वारा धीमा हो जाता है।
ऐसा लगता है कि संकलक बस कि विशिष्ट एल्गोरिथ्म के अनुकूलन में विफल रहता है। –
@ निकलास: ठीक है, हाँ। लेकिन इसके लिए एक कारण होना चाहिए और मुझे आश्चर्य है कि यह क्या है ... – Dave
काम कोड के बिना कुछ भी कहना असंभव है। आपका 'अवशेष' और 'IAtom' प्रकार अपरिभाषित हैं। आपकी 'संरचना' प्रकार की परिभाषा मान्य एफ # कोड भी प्रतीत नहीं होती है। क्या आप एक कामकाजी उदाहरण प्रदान कर सकते हैं? –