PDA

View Full Version : Keyboard Unresponsive/Laggy During Heavy Logic




kingsapo
Feb 14, 2012, 12:16 AM
Hey guys! So I was hoping someone could help me with this little programming issue I've been having. Basically I'm developing an iPhone app to convert various base numbers between one another (binary to octal, for example). I have it set up with multiple text fields, with each field representing a different base system. The user can input into any of these text fields, and the input's converted value will be reflected to the appropriate other text field (for example, if the user typed in 101 in the binary field, the octal field would read 5. Everything works fine, until I get into higher numbers. See, I have it set up so the numbers will convert live as the user types, and due to the heavy amount of string manipulation I have going, it begins to slow the app down as the input number gets longer. And as a result, the keyboard input becomes laggy - especially on older devices - freezing up during the execution of the number conversion code, then becoming fully responsive.

This is kind of an issue, as if a user inputs a large number, it could take a very long time for them to input the whole thing, between typing and waiting for the keyboard to become responsive. Therefore, I wanted to know if there is any way around this? Maybe something which would give priority to the keyboard above everything else? I saw this other app that converts length values in the same UI way that mine does, though in this app, when the numbers get large, the updating of the text fields does slow down, but the keyboard input never gets laggy! It's as if the keyboard is living in its own world, unaffected by whatever other logic is going on in the background.

Please, if anyone knows anything about this, please give me a hand. As always, any help is always appreciated!



rossipoo
Feb 14, 2012, 01:15 AM
Wirelessly posted (Mozilla/5.0 (iPhone; CPU iPhone OS 5_0_1 like Mac OS X) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A405 Safari/7534.48.3)

What sort if string manipulation are you doing, can you post some code? The kind of functionality you're going for should generally be possible to do very fast with no lag at all. Based on the description, I would guess that something very inefficient may be happening.

jonnymo5
Feb 14, 2012, 07:35 AM
Get your processing off the main thread. Look into Grand Central Dispatch. You can then do your processing asynchronously on a different thread and your keyboard should be speedy again.

robvas
Feb 14, 2012, 08:40 AM
Get your processing off the main thread. Look into Grand Central Dispatch. You can then do your processing asynchronously on a different thread and your keyboard should be speedy again.

His algorithim needs work - it should be an instant conversion. Probably some loop that gets bigger and added on each character in the string or something.

kingsapo
Feb 14, 2012, 09:15 AM
Thanks for the replies! Here's my code, since you asked:

- (NSMutableString *)binaryToOctal:(NSMutableString *)input{
NSMutableString *inputString = input; // input string from text field
int numberOfChars = [inputString length]; // number of characters from inputString. used to check to see if input is empty as well as if additional 0's are needed
NSMutableString *finalString = [NSMutableString stringWithString:@""]; // will hold the final formatted octal string

if (numberOfChars != 0) { // if there is no string in the input text field, don't do anything! (will crash otherwise)

NSLog(@"numberOfChars: %i", numberOfChars);

// % is modulus; it divides numberOfChars by 3, then takes the remainder as the result
if (numberOfChars % 3 != 0) { // run test to see if we need to add a 0 to the left side of the inputString
NSLog(@"Invalid string, must append 0(s) to left side");
[inputString insertString:@"0" atIndex:0];
NSLog(@"New String: %@", inputString);
}

numberOfChars = [inputString length]; // refresh the inputString length, since it may have changed from the above test
if (numberOfChars % 3 != 0) { // run the test again, in case we need two 0's at the left side
NSLog(@"Invalid string, must append 0(s) to left side");
[inputString insertString:@"0" atIndex:0];
NSLog(@"New String: %@", inputString);
}

int currentPower = 0; // will track the current power, used below

do {
NSMutableString *currentSet = [NSMutableString stringWithString:[inputString substringFromIndex:[inputString length]-3]]; // takes set of 3 numbers from right side of inputString
NSLog(@"currentSet: %@", currentSet);
inputString = [NSMutableString stringWithString:[inputString substringToIndex:[inputString length]-3]]; // removes set of 3 numbers from right side of inputString by re-creating inputString minus the 3 numbers on the right
int totalFromCurrentSet = 0; // total converted octal value from currentSet

do {
NSString *currentNumber = [currentSet substringFromIndex:[currentSet length]-1]; // takes first number from right side of currentSet
NSLog(@"currentNumber (from currentNumber variable): %@", currentNumber);
if ([currentNumber isEqualToString:@"1"]) { // if currentNumber is 1
NSLog(@"currentNumber detected by if statement as 1");
int x = pow(2, currentPower); // 2^currentPower
NSLog(@"x: %i", x);
totalFromCurrentSet += x; // adds 2^currentPower to totalFromCurrentSet
NSLog(@"new totalFromCurrentSet: %i", totalFromCurrentSet);
currentPower += 1; // incremenets the currentPower, for the next number
NSLog(@"new currentPower: %i", currentPower);
currentSet = [NSMutableString stringWithString:[currentSet substringToIndex:[currentSet length]-1]]; // remove number on right side of currentSet by re-creating currentSet minus the number on the right
NSLog(@"new currentSet: %@", currentSet);
}
if ([currentNumber isEqualToString:@"0"]) {
NSLog(@"currentNumber detected by if statement as 0");
currentPower += 1; // incremenets the currentPower, for the next number
NSLog(@"currentPower: %i", currentPower);
currentSet = [NSMutableString stringWithString:[currentSet substringToIndex:[currentSet length]-1]]; // remove number on right side of currentSet by re-creating currentSet minus the number on the right
NSLog(@"new currentSet: %@", currentSet);
}
} while ([currentSet length] != 0); // only do the above while there are still numbers in the current set

currentPower = 0; // reset the currentPower, so it doesn't get too big for the next set of numbers
NSMutableString *totalFromCurrentSetString = [NSMutableString stringWithFormat: @"%i", totalFromCurrentSet]; // totalFromCurrentSet, created as a string
NSLog(@"totalFromCurrentSetString: %@", totalFromCurrentSetString);
[finalString insertString:totalFromCurrentSetString atIndex:0]; // inserts totalFromCurrentSetString to the right side of finalString

} while ([inputString length] != 0); // only do the above while there are still numbers in the input string

NSLog(@"finalString: %@", finalString);
}



return finalString; // returns the final formatted octal string
}

Like the name implies, this method converts a binary input string into an octal string, then returns it. I think I agree with everybody in that it should be very fast, since all I'm doing is some string manipulation. I'm only a beginner/intermediate Obj-C programmer, so if something inefficient is going on, I probably don't know about it. At first I thought it might be because of memory leaks or something, but I ran the app through Instruments and got no leaks, even while processing the heavy numbers. Let me know if you guys see something. Thanks!

Also thanks jonnymo5. I didn't really want to get into multithreading on this app, but Grand Central sounds like a great last resort.

robbieduncan
Feb 14, 2012, 09:31 AM
I would suggest looking long and hard at your logic and style. Try and eliminate object creation (way too much going on there). Try and make your code re-usable (if you wanted to do binary to base-10 you'd have to re-write most of this).

I'd suggest:

1) Efficiently convert the binary to an int
2) Efficiently convert that int to each of the output formats

Both of these can be done with simple single loop (nested loops are going to cost you) algorithms.

chown33
Feb 14, 2012, 10:33 AM
I don't understand all the uses of NSMutableString. In most cases (maybe even all), you're never actually mutating the string, so there's no benefit to the string being mutable. There is a cost, though: creation cost.

The other thing is the NSLogs. Are they all in there, producing output, when you run your timing tests? Because that much logging, especially in loops, is going to take quite a bit of time. In fact, it wouldn't surprise me at all if the majority of the time is taken by logging.

Also, have you profiled the code? You should probably do it with the NSLogs in there, just to see how much time they take. Then comment them out and rerun it.

Finally, if all that logging is because you don't know how to use the debugger to stop the program, inspect variables, and step through it, then you should learn to use the debugger. That skill alone will make your code far better than anything else is likely to.


And I completely agree with robbieduncan: convert the binary string to an int, then convert that to other formats.

kingsapo
Feb 14, 2012, 09:53 PM
Thank you all so much for your help. robbieduncan, I tried your suggestion of minimizing object creation by putting my NSString and NSMutableString creations on the outside of my loop, and that didn't help much. Then I tried chown33's suggestion on NSLogs, and now the program SCREAMS!!! Tested on an iPhone 4S and an iPhone 3G. The 4S is obviously faster than the 3G, but the 3G would become unresponsive at times with all those NSLogs in there. It never occurred to me that NSLogs could slow a program down, but now I know!

chown33, I will also learn to better utilize the debugger. If it can do like you said and step through an analyze each variable, then it seems like a great alternative to NSLog. It seems like NSLog is ok for a quick debugger-less solution, but shouldn't be in the final program.

I'm also going to take everybody's advice by re-writing the method to better utilize ints rather than strings.

Thanks you again! I'd be really stuck without you right now! :D

PS: Sorry this thread got kinda off-topic - for anybody who came here looking to make the keyboard more responsive, you should give jonnymo5's solution a look.

kingsapo
Feb 14, 2012, 10:29 PM
Really sorry to do this, but I have another issue. You guys suggested to convert the input string into an int, then convert that int into another base format. That sounds great, except that ints, as well as every numeric data type of Objective-C that I know of have limits, the limit that my program showing me for int being 2147483647. I'd really love to make this app faster and more optimized, but I just can't limit users to entering less than 10 digits for a binary value. Any ideas?

chown33
Feb 14, 2012, 11:45 PM
Really sorry to do this, but I have another issue. You guys suggested to convert the input string into an int, then convert that int into another base format. That sounds great, except that ints, as well as every numeric data type of Objective-C that I know of have limits, the limit that my program showing me for int being 2147483647. I'd really love to make this app faster and more optimized, but I just can't limit users to entering less than 10 digits for a binary value. Any ideas?

10 digits of binary is 10 bits. The largest value that fits in 10 bits is 1023 (decimal). Enter it into a calculator: it's 2 to the 10th power, minus 1.

2147483647 is a decimal value (base 10). It's the upper limit for a signed 32-bit int. Obviously, a 32-bit int has 32 bits, i.e. 32 binary digits. That's what a bit is: a binary digit (http://en.wikipedia.org/wiki/Bit).

The largest C type you can use for holding integers is unsigned long long. It's 64 bits.

If 64 bits still isn't enough, look up multi-precision arithmetic in any decent programming reference book. Or look it up on Wikipedia. It's not difficult for something as simple as basic conversions, but you do need a firm grasp of the fundamentals, which you frankly seem to be lacking. You might want to spend a little more time studying exactly how an int or long long represents numbers, before you try writing code for it.

mobilehaathi
Feb 15, 2012, 12:52 AM
I have a feeling that few users will query numbers larger than 18446744073709551615.

kingsapo
Feb 15, 2012, 10:50 PM
chown33, thanks for your help (again). I understand some of what you're saying, but you're right when you said I should learn how ints and longs and stuff actually represent their numbers. I'll give what you suggested a look. Thanks again! :D

mobilehaathi, thanks for the post, but I don't think you understand the purpose of my program. The program will convert whatever the user types from binary into whatever (octal, hex, etc.). Therefore, it is entirely possible for a user to enter a string with a length bigger than the number you gave, but still have the program freak out because it is bigger than the max value of an int or long or whatever (again, I don't know much about it). Just thought I'd clarify.

chown33
Feb 15, 2012, 11:50 PM
mobilehaathi, thanks for the post, but I don't think you understand the purpose of my program. The program will convert whatever the user types from binary into whatever (octal, hex, etc.). Therefore, it is entirely possible for a user to enter a string with a length bigger than the number you gave, but still have the program freak out because it is bigger than the max value of an int or long or whatever (again, I don't know much about it). Just thought I'd clarify.

Write the program properly for 64-bits. If done properly, it won't "freak out" (whatever that means). Worst case, you tell the user that the number they entered exceeds the precision of the program.

You really need to focus on making it work for the simple case first. Then you can improve it. But if you don't understand how the simple case works, then you're not really in a position to do anything about the complex one.