madispJRebel for Android•

why not just use switch statement over strings and emit Java 7 bytecode? Dexer will allow it and you'll get a bytecode implementation of a hashtable.

16
0x800OP•

Excellent point, and you are absolutely right - the performance would actually be better:

The Java compiler generates generally more efficient bytecode from switch statements that use String objects than from chained if-then-else statements.

This is entirely my fault - I’m still stuck in the Java 6 era.

11
madispJRebel for Android•

yup, and if you look at the bytecode then it'll end up being a switch over String.hashCode() and then a chain of .equals() calls. That's pretty much a hashtable. (See http://stackoverflow.com/questions/10836055/why-is-the-switch-statement-faster-than-if-else-for-string-in-java-7).

7
0x800OP•

Hi guys, author here. I’ll be around, so feel free to ask me any questions you may have.

Here’s the link to the library and comments from last week.

Let’s be a bit more more constructive this time - if you say A is better than B, or use C instead of D, or this is shit because of E, please provide some context or references backing your claim. Also, please don’t do code reviews/pull requests on auto-generated code. :)

14
dccorona•

Did you at any point consider that maybe the static interface you went with was contributing to the reason you had these memory concerns in the first place? I assume the reason you were concerned about the memory overhead was because, being a static API, you had to either continually keep it in memory whether it was actually in use or not, or load it from disk for every access.

Had you ever thought about making it a constructed object, thereby leaving the memory usage concern (somewhat) on the end user...if they care, they'll construct it, cache what they actually need, and leave it to be garbage collected.

12
0x800OP•

Yes, your assumptions are correct and you have an excellent point/suggestion! I actually love this idea. I guess this hasn’t crossed my mind because I was trying to solve a particular problem, and with this approach, I’d still be facing it.

But to use your approach in future updates, maybe a factory offering either an efficient memory implementation, an efficient search implementation, or something in between would be useful and easy to implement. As you pointed out, it's then up to the user to do with the object whatever he or she pleases.

Hey, this is great, thank you very much for the insight!

7
jrummy16•

This handles a lot more than 400 devices: http://stackoverflow.com/a/27836910/1048340

2
0x800OP•

It also consumes about 14 kB of memory. There’s nothing wrong with that, of course, it’s just set up with a different goal in mind.

2
ProfWhite•

Valid point; playing devil's advocate, it also takes O(1) time instead of O(N). Now, for a smaller set (400ish) that's not a big deal - the difference is negligible, enough so that I'd almost say it shouldn't be a consideration. Just something to keep in mind I guess.

I'm all for having more options for things. Thanks for your work on this!

2
merreborn•

Factors to consider:

  • memory consumption
  • execution time
  • code maintainability

Given this class is loaded once, and executed once, I'm not sure memory or execution time were really problems with the .properties implementation. How much did that 60k of ram really hurt? And notably: the .properties implementation didn't require a python build script.

You may have committed the sin of premature optimization

Programmers waste enormous amounts of time thinking about, or worrying about, the speed of noncritical parts of their programs, and these attempts at efficiency actually have a strong negative impact when debugging and maintenance are considered. We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. Yet we should not pass up our opportunities in that critical 3%.

And after all that, you landed at an O(n) implementation. If you're going to optimize the snot out of this just for fun, don't stop there! Maybe get fancy, and store a compressed trie data structure in your class?

In conclusion, you've either gone too far, or not far enough ;)

11
BostonTentacleParty•

It's a library. People using libraries need to be able to safely assume they're well optimized. I don't think writing libraries applies to that quote there.

16
0x800OP•

Oh yes, I absolutely agree, this is as premature as it gets :) my side project was moving slow and this case study was definitely born out of procrastination :) As you have noted, I snapped out of it just before things were starting to heat up (and go beyond my level of expertise). :) Thanks for your input, I'll go ahead and try the fancy stuff over the weekend.

4
prlmike•

If your library slows down my app startup by more than 50ms, it better be more than a nice to have.

2
merreborn•

Sure. The OP never states any of the implementations took anywhere near that long to load though -- which is what makes the optimization premature.

By all means, if he'd had evidence this was taking >50ms in any incarnation, that would be just cause for optimization.

1
bubbleguuum•

I suggest using enums.

11
0x800OP•
3
40ft•

For an immutable list lookup, with pre-processing available, here's another method you can use:

  • write a custom hash function which converts the model name into an integer n where 0 <= n < 400 + x, the hash function has zero collisions, and x is minimized. This function is left as an exercise for the reader.
  • create a flat file which contains all the name mappings. This can either have an index thus allowing variable length strings and minimizing the file size, or no index which allows slightly faster lookup but at a larger file size. This file is a very simple database-esque file.
  • the lookup method then hashes the model name, producing an integer which is byte offset into the file, allowing random access.

This is very fast, and has zero memory overhead, and a small code footprint.

9
0x800OP•

Great suggestion, I’ll definitely look into it. Thanks!

1
meTa_AU•

Combine it with cuckoo hashing to maximise occupancy (minimise x)

Edit: Or the CHD algorithm (which I think gets 99% occupancy)

2
b1ackcat•

Thanks a lot for posting this. This is a great write-up and it's clear you put a lot of thought into how and why you structured your project the way you did.

Pay no mind to the peanut gallery. Everyone can always find issues with code they themsleves didn't write within the last 24 hours, simply because it's "not how they would've done it".

And thanks for the library!

7
markerz•

I actually like that he paid attention to the peanut gallery. Now we can have a discussion on what is the better implementation and what the definition of better is.

9
b1ackcat•

Oh definitely! I just meant to not take it personally. Sorry, should've been more clear.

3
0x800OP•

Hey, thank you very much for your kind words, I really appreciate it.

3
dell_support•

How about keep all in array, sorted, then use binary search? O(log n) Am I right or I missed something? It takes memory but it will search faster thank picking first letter.

6
0x800OP•

You are right, that would be a good, viable solution. But I was willing to sacrifice CPU time in order to avoid memory overhead.

3
pinusc•

You could even try to implement binary search on your class - replacing the if. You wouldn't get memory overhead and you could have better speed. But I don't really know if this could be an improvement. Surely it isn't easy to implement as a simple method call.

1
NotJamesFranco•

Really cool! I always appreciate case studies like this, this was the type of stuff in college that I was really passionate about. Great write-up with solid empirical reasoning behind it.

3
0x800OP•

Many thanks, I’m glad you like it.

1
8lbIceBag•

If you want to do this pragmatically, why not do it on a per manufacturer basis? The advantaged being you don't have things hard coded and you should be able to decode future models with some accuracy.

Pseudo-code (No language like this exists):

Public Structure DeviceInfo {
	Public sManufacturer as String = "Samsung"
	Public sLine as String = "Galaxy"
	Public sModel as String = "S"
	Public sSubModel as String = "Active"
	Public sEdition as String ="IV"
	Public nEdition as Integer = 4
}

Public Static Function getDeviceInfo() as DeviceInfo {		
	dim oDeviceInfo as new DeviceInfo
	oDeviceInfo.sManufacturer = android.os.Build.MANUFACTURER
	Switch(oDeviceInfo.sManufacturer){	
		Case: "SAMSUNG"
			 loadSamsungDevice(oDeviceInfo)
		Case: ...
	}
}

Private Static Subroutine loadSamsungDevice(ByReference *oDeviceInfo as DeviceInfo) {		
	if(android.os.Build.MODEL.startsWith("GT-"){
		oDeviceInfo.sLine="Galaxy"		
		if(android.os.Build.MODEL.substringAfter("GT-").startsWith("I")) {
				etc....
		}
	}	
}

Then the app consuming the library can put it all together.

2
_danada•

As an aside, a few months ago I dumped the list of devices from the Google Play Dev Console so I could create a lookup for my application. Let me know if you want the dump (it's something like 2400 devices in total).

2
0x800OP•

I believe you’re talking about this? If so, I’m covered, but thank you for offering to help!

1
[deleted]•

[deleted]

-3