Swift Array Performance Issue? - ios

I'm not sure if there is an issue or not, so i'm just gonna write it down.
I'm developing using swift, xcode 7.2 , on iphone 5s.
And calculating execution time using
NSDate.timeIntervalSinceReferenceDate()
I created 2 arrays, one with 200,000 elements and one with 20.
and try to have random access to their elements. accessing elements on big one is almost 55 times slower! i know its bigger but isn't this O(1) ?
I also tried the same on java and the accessing speed is the same for big and small array.
From CFArrayheader in apple documentation, i found this:
Accessing any value at a particular index in an array is at worst O(log n), but should usually be O(1).
but it think this cant be true based on the numbers i've tested.
I know i didn't make a big test or anything special, but the fact that its not working is really messing with my head!
i kinda need this for what i'm working on. and the algorithm is not working on swift and iOS and its working on java and android.
let bigSize:Int = 200000
var bigArray = [Int](count:bigSize,repeatedValue:0)
let smallSize:Int = 20
var smallArray = [Int](count:smallSize,repeatedValue:0)
for i in 0..<bigSize
{
bigArray[i] = i + 8 * i
}
for i in 0..<smallSize
{
smallArray[i] = i + 9 * i
}
let indexBig = Int(arc4random_uniform(UInt32(bigSize)) % UInt32(bigSize))
let indexSmall = Int(arc4random_uniform(UInt32(smallSize)) % UInt32(smallSize))
var a = NSDate.timeIntervalSinceReferenceDate()
print(bigArray[indexBig])
var b = NSDate.timeIntervalSinceReferenceDate()
print(b-a) \\prints 0.000888049602508545
a = NSDate.timeIntervalSinceReferenceDate()
print(smallArray[indexSmall])
b = NSDate.timeIntervalSinceReferenceDate()
print(b-a) \\prints 6.90221786499023e-05
java :
(accessing one element is so fast on java and its on pc, so i access more elements, but same number on both arrays)
int bigSize = 200000;
int[] bigArray = new int[bigSize];
Random rand = new Random();
int smallSize = 20;
int[] smallArray = new int[smallSize];
for(int i = 0;i < bigSize;i++)
bigArray[i] = i + i * 8;
for(int i = 0;i < smallSize;i++)
smallArray[i] = i + i * 8;
int smallIndex = rand.nextInt(smallSize);
int bigIndex = rand.nextInt(bigSize);
int sum = 0;
long a = System.currentTimeMillis();
for(int i = 0;i < 10000;i++)
{
sum += bigArray[rand.nextInt(bigSize)];
}
System.out.println(sum);
long b = System.currentTimeMillis();
System.out.println(b-a); //prints 2
a = System.currentTimeMillis();
sum = 0;
for(int i = 0; i < 10000;i++)
{
sum += smallArray[rand.nextInt(smallSize)];
}
System.out.println(sum);
b = System.currentTimeMillis();
System.out.println(b - a); //prints 1

If you change the order of your two tests, you'll find that the performance is flipped. In short, the first test runs more slowly than the second one, regardless of whether it's the small array or the big one. This is a result of some dynamics of print. If you do a print before you perform the tests, the delay resulting from the first print is eliminated.
A better way to test this would be to create a unit test, which (a) repeats the subscript operator many times; and (b) uses measureBlock to repeat the test a few times to check for standard deviation and the like.
When I do that, I find the access time is indistinguishable, consistent with O(1). This were my unit tests:
let bigSize: Int = 200_000
let smallSize: Int = 20
func testBigArrayPerformance() {
let size = bigSize
let array = Array(0 ..< size).map { $0 + 8 * $0 }
var value = 0
measureBlock {
let baseIndex = Int(arc4random_uniform(UInt32(size)))
for index in 0 ..< 1_000_000 {
value += array[(baseIndex + index) % size]
}
}
print(value)
print(array.count)
}
func testSmallArrayPerformance() {
let size = smallSize
let array = Array(0 ..< size).map { $0 + 8 * $0 }
var value = 0
measureBlock {
let baseIndex = Int(arc4random_uniform(UInt32(size)))
for index in 0 ..< 1_000_000 {
value += array[(baseIndex + index) % size]
}
}
print(value)
print(array.count)
}
Admittedly, I've added some mathematical operations that change the index (my intent was to make sure the compiler didn't do some radical optimization that removed my attempt to repeat the subscript operation), and the overhead of that mathematical operation will dilute the subscript operator performance difference. But, even when I simplified the index operator, the performance between the two renditions was indistinguishable.

Related

Most effecient way to calculate this in swift (simple math)

The question is related to calculating an increase in currency.
Loop over this n times, and let's say you start with $50k and your multiplier is 2. Something like b * 2 + a
This is the correct result:
$50,000.00
$100,000.00
$250,000.00
$600,000.00
$1,450,000.00
$3,500,000.00
$8,450,000.00
$20,400,000.00
$49,250,000.00
So just to be clear, the question is about efficiency in swift, not simply how to calculate this. Are there any handy data structures that would make this faster? Basically I was just looping through how many years (n) adding 2 (200%) and incrementing a couple temp variables to keep track of the current and previous values. It feels like there has got to be a much better way of handling this.
$50k base
$50k * 2 + 0 (previous value) = $100k
$100k * 2 + $50k = $250k
$250k * 2 + $100k = $600k
etc.
Code:
let baseAmount = 50000.0
let percentReturn = 200.0
let years = 10
// Calc decimal of percent.
var out: Double = 0.0
var previous: Double = 0.0
let returnPercent = percentReturn * 0.01
// Create tmp array to store values.
var tmpArray = [Double]()
// Loop through years.
for var index = 0; index < years; ++index
{
if index == 0
{
out = baseAmount
tmpArray.append(baseAmount)
}
else if index == 1
{
out = (out * returnPercent)
tmpArray.append(out)
previous = baseAmount
}
else
{
let tmp = (tmpArray.last! * returnPercent) + previous
previous = tmpArray.last!
tmpArray.append(tmp)
}
}
println(tmpArray)
Here are some ideas for improving efficiency:
Initialize your array to the appropriate size (it isn't dynamic; it is always the number of years)
Remove special cases (year 0 and 1 calculations) from the for-loop
Code:
func calculate(baseAmount: Double, percentReturn: Double, years: Int) -> [Double] {
// I prefer to return an empty array instead of nil
// so that you don't have to check for nil later
if years < 1 {
return [Double]()
}
let percentReturnAsDecimal = percentReturn * 0.01
// You know the size of the array, no need to append
var result = [Double](count: years, repeatedValue: 0.0)
result[0] = baseAmount
// No need to do this in the loop
if years > 1 {
result[1] = baseAmount * percentReturnAsDecimal
}
// Loop through years 2+
for year in 2 ..< years {
let lastYear = result[year - 1]
let yearBeforeLast = result[year - 2]
result[year] = (lastYear * percentReturnAsDecimal) + yearBeforeLast
}
return result
}
Efficiency in terms of speed I found this to be the fastest implementation of your algorithm:
let baseAmount = 50000.0
let returnPercent = 2.0
let years = 10
// you know the size of the array so you don't have to append to it and just use the subscript which is much faster
var array = [Double](count: years, repeatedValue: 0)
var previousValue = 0.0
var currentValue = baseAmount
for i in 0..<years {
array[i] = currentValue
let p2 = currentValue
currentValue = currentValue * returnPercent + previousValue
previousValue = p2
}
print(array)

UIImage Loop Through Pixel Highly Inefficient?

Currently I am using this method to loop through every pixel, and insert a value into a 3D array based upon RGB values. I need this array for other parts of my program, however it is extraordinarily slow. When run on a 50 x 50 picture, it is almost instant, but as soon as you start getting into the hundreds x hundreds it takes a long time to the point where the app is useless. Anyone have any ideas on how to speed up my method?
#IBAction func convertImage(sender: AnyObject) {
if let image = myImageView.image {
var pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage))
var data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
let height = Int(image.size.height)
let width = Int(image.size.width)
var zArry = [Int](count:3, repeatedValue: 0)
var yArry = [[Int]](count:width, repeatedValue: zArry)
var xArry = [[[Int]]](count:height, repeatedValue: yArry)
for (var h = 0; h < height; h++) {
for (var w = 0; w < width; w++) {
var pixelInfo: Int = ((Int(image.size.width) * Int(h)) + Int(w)) * 4
var rgb = 0
xArry[h][w][rgb] = Int(data[pixelInfo])
rgb++
xArry[h][w][rgb] = Int(data[pixelInfo+1])
rgb++
xArry[h][w][rgb] = Int(data[pixelInfo+2])
}
}
println(xArry[20][20][1])
}
}
Maybe there is a way to convert the UIImage to a different type of image and create an array of pixels. I am open to all suggestions. Thanks!
GOAL: The goal is to use the array to modify the RGB values of all pixels, and create a new image with the modified pixels. I tried simply looping through all of the pixels without storing them, and modifying them into a new array to create an image, but got the same performance issues.
Update:
After countless tries I realized I was making my tests on debug configuration.
Switched to release, and now it's so much faster.
Swift seems to be many times slower on the debug configuration.
The difference now between your code and my optimized version is several times faster.
It seems as you have a big slowdown from using image.size.width instead of the local variable width.
Original
I tried to optimize it a bit and come up with this:
#IBAction func convertImage () {
if let image = UIImage(named: "test") {
let pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage))
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
let height = Int(image.size.height)
let width = Int(image.size.width)
let zArry = [Int](count:3, repeatedValue: 0)
let yArry = [[Int]](count:width, repeatedValue: zArry)
let xArry = [[[Int]]](count:height, repeatedValue: yArry)
for (index, value) in xArry.enumerate() {
for (index1, value1) in value.enumerate() {
for (index2, var value2) in value1.enumerate() {
let pixelInfo: Int = ((width * index) + index1) * 4 + index2
value2 = Int(data[pixelInfo])
}
}
}
}
}
However in my tests this is barely 15% faster. What you need is orders of magnitude faster.
Another ideea is use the data object directly when you need it without creating the array like this:
let image = UIImage(named: "test")!
let pixelData = CGDataProviderCopyData(CGImageGetDataProvider(image.CGImage))
let data: UnsafePointer<UInt8> = CFDataGetBytePtr(pixelData)
let width = Int(image.size.width)
// value for [x][y][z]
let value = Int(data[((width * x) + y) * 4 + z])
You didn't say how you use this array in your app, but I feel that even if you find a way to get this array created much faster, you would get another problem when you try to use it, as it would take a long time too..

Generate a Swift array of nonrepeating random numbers

I'd like to generate multiple different random numbers in Swift. Here is the procedure.
Set up an empty array
Generate a random number
Check if the array is empty
a. If the array is empty, insert the random number
b. If the array is not empty, compare the random number to the numbers in array
i. If the numbers are the same, repeat 2
ii. if the numbers are not the same, insert the random number and repeat 2
import UIKit
//the random number generator
func randomInt(min: Int, max:Int) -> Int {
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
}
var temp = [Int]()
for var i = 0; i<4; i++ {
var randomNumber = randomInt(1, 5)
if temp.isEmpty{
temp.append(randomNumber)
} else {
//I don't know how to continue...
}
}
If you use your method the problem is, that you will create a new random-number each time. So you possibly could have the same random-number 4 times and so your array will only have one element.
So, if you just want to have an array of numbers from within a specific range of numbers (for example 0-100), in a random order, you can first fill an array with numbers in 'normal' order. For example with for loop etc:
var min = 1
var max = 5
for var i = min; i<= max; i++ {
temp.append(i)
}
After that, you can use a shuffle method to shuffle all elements of the array with the shuffle method from this answer:
func shuffle<C: MutableCollectionType where C.Index == Int>(var list: C) -> C {
let count = countElements(list)
for i in 0..<(count - 1) {
let j = Int(arc4random_uniform(UInt32(count - i))) + i
swap(&list[i], &list[j])
}
return list
}
Ater that you can do something like that:
shuffle(temp) // e.g., [3, 1, 2, 4, 5]
The construct you’re looking for with your approach might be something like:
var temp: [Int] = []
while temp.count < 4 {
var randomNumber: Int
do {
randomNumber = randomInt(1, 5)
} while contains(temp, randomNumber)
temp.append(randomNumber)
}
This will be fine for tiny ranges like yours, but for larger ranges it will be very slow, because for the last few numbers you are waiting for the random number to hit precisely the remaining handful of possibilities. I just tried generating from a range of 200 in a playground and it took 9 seconds.
If you want a random selection of numbers with guaranteed coverage over a range, you could generate it like by taking that range and shuffling it, like this:
func shuffle<S: SequenceType>(source: S) -> [S.Generator.Element] {
var rangen = GeneratorOf { arc4random() }
let a = Array(Zip2(rangen, source))
return a.sorted { $0.0 < $1.0 }.map { $0.1 }
}
let min = 1, max = 5
shuffle(min...max)
If you want a selection of n non-repeating random numbers from a range 0..<m, there’s a particularly pretty algorithm to do this that generates an ascending sequence of random numbers from that range:
func randomGeneratorOf(#n: Int, #from: Int) -> GeneratorOf<Int> {
var select = UInt32(n)
var remaining = UInt32(from)
var i = 0
return GeneratorOf {
while i < from {
if arc4random_uniform(remaining) < select {
--select
--remaining
return i++
}
else {
--remaining
++i
}
}
return nil
}
}
Which you could use like so:
let engines = [
"Duck","Emily","Gordon","Henry", "Mavis",
"Belle","James","Edward","Thomas","Toby"
]
let picks = Array(randomGeneratorOf(n: 3, from: engines.count))
for engine in PermutationGenerator(elements: engines, indices: picks) {
println(engine)
}
Below is my suggestion.
I like this way since it is short and simple :)
let totalCount: Int = 150 //Any number you asssign
var randomNumArray: [Int] = []
var i = 0
while randomNumArray.count < totalCount {
i++
let rand = Int(arc4random_uniform(UInt32(totalCount)))
for(var ii = 0; ii < totalCount; ii++){
if randomNumArray.contains(rand){
print("do nothing")
} else {
randomNumArray.append(rand)
}
}
}

Create Loop for Amortization Schedule in Swift

I'm looking to figure out a simple loop in order to calculate an amortization schedule in Swift.
So far, here is my setup on Playground:
let loanAmount: Double = 250000.00
let intRate: Double = 4.0
let years: Double = 30.0
var r: Double = intRate / 1200
var n: Double = years * 12
var rPower: Double = pow(1 + r, n)
var monthlyPayment: Double = loanAmount * r * rPower / (rPower - 1)
var annualPayment: Double = monthlyPayment * 12
For the actual loop, I'm unsure how to fix the code below.
for i in 0...360 {
var interestPayment: Double = loanAmount * r
var principalPayment: Double = monthlyPayment - interestPayment
var balance: Double; -= principalPayment
}
Looking to generate a monthly schedule. Thanks in advance for any tip.
I'm guessing you mean to declare the balance variable outside the loop, and to decrement it inside the loop:
// stylistically, in Swift it's usual to leave
// off the types like Double unless you have a
// reason to be explicit
let loanAmount = 250_000.00
let intRate = 4.0
let years = 30.0
// since these are one-off calculations, you
// should use let for them, too. let doesn't
// just have to be for constant numbers, it just
// means the number can't change once calculated.
let r = intRate / 1200
let n = years * 12
let rPower = pow(1 + r, n)
// like above, these aren't changing. always prefer let
// over var unless you really need to vary the value
let monthlyPayment = loanAmount * r * rPower / (rPower - 1)
let annualPayment = monthlyPayment * 12
// this is the only variable you intend to "vary"
// so does need to be a var
var balance = loanAmount
// start counting from 1 not 0 if you want to use an open
// (i.e. including 360) range, or you'll perform 361 calculations:
for i in 1...360 {
// you probably want to calculate interest
// from balance rather than initial principal
let interestPayment = balance * r
let principalPayment = monthlyPayment - interestPayment
balance -= principalPayment
println(balance)
}
This should print out the correct balances going down to zero for the final balance (well actually 9.73727765085641e-09 – but that's a whole other question).
If you wanted to create a monthly balance, say in an array, you could add an additional array variable to store that in:
var balance = loanAmount
//array of monthly balances, with the initial loan amount to start with:
var monthlyBalances = [balance]
for i in 1...360 {
let interestPayment = balance * r
let principalPayment = monthlyPayment - interestPayment
balance -= principalPayment
monthlyBalances.append(balance)
}
Advanced version for anyone who's interested
You might wonder if there's a way to declare monthlyBalances with let rather than var. And there is! You could use reduce:
let monthlyBalances = reduce(1...360, [loanAmount]) {
payments, _ in
let balance = payments.last!
let interestPayment = balance * r
let principalPayment = monthlyPayment - interestPayment
return payments + [balance - principalPayment]
}
However this is a bit nasty for a couple of reasons. It would much much nicer if the Swift standard library had a slightly different version of reduce called accumulate that generated an array out of a running total, like this:
let monthlyBalances = accumulate(1...360, loanAmount) {
balance, _ in
let interestPayment = balance * r
let principalPayment = monthlyPayment - interestPayment
return balance - principalPayment
}
And here's a definition of accumulate:
func accumulate<S: SequenceType, U>
(source: S, var initial: U, combine: (U, S.Generator.Element) -> U)
-> [U] {
var result: [U] = []
result.append(initial)
for x in source {
initial = combine(initial, x)
result.append(initial)
}
return result
}

how to compare a variable to entries inside an Array

Here is my problem.
I'm doing a little weather web app in flash.
So i read an xml feed and copose an array of data from it.
my xml is like this
<xml>
<prevision>
<date>22</date>
<hour>5</hour>
<temperature>40</temperature>
</prevision>
<prevision>
<date>22</date>
<hour>10</hour>
<temperature>44</temperature>
</prevision>
<prevision>
<date>22</date>
<hour>14</hour>
<temperature>45</temperature>
</prevision>
<prevision>
<date>22</date>
<hour>20</hour>
<temperature>37</temperature>
</prevision>
</xml>
So here is what i'm doing with my Actionscript 2
//**************************************
// Here i'm getting the current hour
var mytime = new Date();
var currenthour = mytime.getHours();
//*************************************
// Here i'm getting my XML
var myhour:Array = new Array();
var myxml:XML = new XML();
myxml.ignoreWhite = true;
myxml.onLoad = function(success)
{
dataextractor = myxml.firstChild.childNodes;
for (var j = 0; j < dataextractor.length; j++)
{
myhour.push(dataextractor[j].childNodes[1].firstChild.nodeValue);
}
// Doing this
trace(mydate);
}
// Will return this
// 5 , 10, 14 , 20
Now what i would like to do is to find and trace the entry in mydate array which have a a value that approach the most to "currenthour" variable (which is my current time i'm getting upper).
For exemple if currenthour = 11
In the case that i have myhour array equal to 5 , 10 , 14 , 20
The best match will be myhour[1]
Can someone help to do so?
myhour Array don't have a fixed number of entries.
Sometime it can be 5, 10, 14 and other time just 14,20 etc
Thanks a lot,
I'm not sure, if I got your idea. Do you need to find the nearest number to currenthour from myhour array?
That's it:
var minDiff : int = 12;
var diff : int;
var index : int = 0;
for (var i : int = 0; i < myhour.length; i++)
{
diff = Math.abs(myhour[i] - currenthour);
if (diff > 12)
diff = 24 - diff;
if (diff < minDiff)
{
minDiff = diff;
index = i;
}
}
myhour[index] is the result. Hope, I got an idea.
Imagine your numbers on a line, from 0 to 24.
Using a variable to keep track of the smallest distance between the current hours and each hour in your XML, you can find the index of the node you're looking for:
import mx.xpath.XPathAPI;
var myxml:XML = new XML();
myxml.ignoreWhite = true;
myxml.onLoad = function(loaded:Boolean) {
if (loaded) {
var hoursMin:Number = 24;
var hoursNow:Number = new Date().getUTCHours();
var hourID:Number;
var hours:Array = XPathAPI.selectNodeList(this.firstChild, "xml/prevision/hour");
var hoursNum:Number = hours.length;
for(var i:Number = 0 ; i < hoursNum; i++){
var hoursDiff:Number = Math.abs(hoursNow - parseInt(hours[i].firstChild));//look for the 'shortest distance' within 24 numbers
if(hoursDiff < hoursMin){//found the smallest current value
hoursMin = hoursDiff;//update the minimum
hourID = i;//store the node ID
}
}
trace(this.firstChild.childNodes[hourID]);//access the closest node in time
} else {
trace("XML Load Error!!");
}
}
myxml.load("feed.xml");
Here's the same thing without XPath, although I find accessing nodes a bit harder this way:
var myxml:XML = new XML();
myxml.ignoreWhite = true;
myxml.onLoad = function(loaded:Boolean) {
if (loaded) {
var hoursMin:Number = 24;
var hoursNow:Number = new Date().getUTCHours();
var hourID:Number;
var hoursNum:Number = this.firstChild.childNodes.length;
for(var i:Number = 0 ; i < hoursNum; i++){
var hoursDiff:Number = Math.abs(hoursNow - parseInt(this.firstChild.childNodes[i].childNodes[1].firstChild));//look for the 'shortest distance' within 24 numbers
if(hoursDiff < hoursMin){//found the smallest current value
hoursMin = hoursDiff;//update the minimum
hourID = i;//store the node ID
}
}
trace(this.firstChild.childNodes[hourID]);//access the closest node in time
} else {
trace("XML Load Error!!");
}
}
myxml.load("feed.xml");
HTH

Resources