performance - Swift Dictionary slow even with optimizations: doing uncessary retain/release? -
the following code, maps simple value holders booleans, runs on 20x faster in java swift 2 - xcode 7 beta3, "fastest, aggressive optimizations [-ofast]", , "fast, whole module optimizations" turned on. can on 280m lookups/sec in java 10m in swift.
when @ in instruments see of time going pair of retain/release calls associated map lookup. suggestions on why happening or workaround appreciated.
the structure of code simplified version of real code, has more complex key class , stores other types (though boolean actual case me). also, note using single mutable key instance retrieval avoid allocating objects inside loop , according tests faster in swift immutable key.
edit: have tried switching nsmutabledictionary when used swift objects keys seems terribly slow.
edit2: have tried implementing test in objc (which wouldn't have optional unwrapping overhead) , faster still on order of magnitude slower java... i'm going pose example question see if has ideas.
edit3 - answer. have posted conclusions , workaround in answer below.
public final class mykey : hashable { var xi : int = 0 init( _ xi : int ) { set( xi ) } final func set( xi : int) { self.xi = xi } public final var hashvalue: int { return xi } } public func == (lhs: mykey, rhs: mykey) -> bool { if ( lhs === rhs ) { return true } return lhs.xi==rhs.xi } ... var map = dictionary<mykey,bool>() let range = 2500 x in 0...range { map[ mykey(x) ] = true } let runs = 10 _ in 0...runs { let time = time() let reps = 10000 let key = mykey(0) _ in 0...reps { x in 0...range { key.set(x) if ( map[ key ] == nil ) { xctasserttrue(false) } } } print("rate=\(time.rate( reps*range )) lookups/s") }
and here corresponding java code:
public class mykey { public int xi; public mykey( int xi ) { set( xi ); } public void set( int xi) { this.xi = xi; } @override public int hashcode() { return xi; } @override public boolean equals( object o ) { if ( o == ) { return true; } mykey mk = (mykey)o; return mk.xi == this.xi; } } ... map<mykey,boolean> map = new hashmap<>(); int range = 2500; for(int x=0; x<range; x++) { map.put( new mykey(x), true ); } int runs = 10; for(int run=0; run<runs; run++) { time time = new time(); int reps = 10000; mykey buffer = new mykey( 0 ); (int = 0; < reps; it++) { (int x = 0; x < range; x++) { buffer.set( x ); if ( map.get( buffer ) == null ) { assert.asserttrue( false ); } } } float rate = reps*range/time.s(); system.out.println( "rate = " + rate ); }
after experimentation have come conclusions , found workaround (albeit extreme).
first let me recognize kind of fine grained data structure access within tight loop not representative of general performance, affect application , i'm imagining others games , heavily numeric applications. let me know swift moving target , i'm sure improve - perhaps workaround (hacks) below not necessary time read this. if trying today , looking @ instruments , seeing majority of application time spent in retain/release , don't want rewrite entire app in objc please read on.
what have found almost 1 in swift touches object reference incurs arc retain/release penalty. additionally optional values - optional primitives - incur cost. pretty rules out using dictionary or nsdictionary.
here things fast can include in workaround:
a) arrays of primitive types.
b) arrays of final objects long as long array on stack , not on heap. e.g. declare array within method body (but outside of loop of course) , iteratively copy values it. not array(array) copy it.
putting can construct data structure based on arrays stores e.g. ints , store array indexes objects in data structure. within loop can objects index in fast local array. before ask "couldn't data structure store array me" - no, because incur 2 of penalties mentioned above :(
all things considered workaround not bad - if can enumerate entities want store in dictionary / data structure should able host them in array described. using technique above able exceed java performance factor of 2x in swift in case.
if still reading , interested @ point consider updating example code , posting.
edit: i'd add option: c) possible use unsafemutablepointer<> or unmanaged<> in swift create reference not retained when passed around. not aware of when started , hesitate recommend in general because it's hack, i've used in few cases wrap heavily used array incurring retain/release every time referenced.
Comments
Post a Comment