Why Was "Avoid Enums Where You Only Need Ints" Removed from Android's Performance Tips

Why was Avoid Enums Where You Only Need Ints removed from Android's performance tips?

The 2011 answer from Elliot Hugues said that the original reason to avoid enum was for performance reason... as in "processing performance". As this reason was not backed by fact, it was removed from the official documentation.

It has been added later on because enums add a lot more data in memory than using integer.

Why doesn't Android use more enums?

This answer is out of date as of March 2011.

Enums can be used on Froyo and up - according to this answer (Why was “Avoid Enums Where You Only Need Ints” removed from Android's performance tips?) from a member of the Android VM team (and his blog).


Previous Answer:

The official Android team recommendation is to avoid enums whenever you can avoid it:

Enums are very convenient, but
unfortunately can be painful when size
and speed matter. For example, this:

public enum Shrubbery { GROUND, CRAWLING, HANGING }

adds 740 bytes to
your .dex file compared to the
equivalent class with three public
static final ints. On first use, the
class initializer invokes the
method on objects representing each of
the enumerated values. Each object
gets its own static field, and the
full set is stored in an array (a
static field called "$VALUES"). That's
a lot of code and data, just for three
integers. Additionally, this:

Shrubbery shrub = Shrubbery.GROUND;

causes a static field lookup. If
"GROUND" were a static final int, the
compiler would treat it as a known
constant and inline it.

Source: Avoid Enums Where You Only Need Ints

Should I strictly avoid using enums on Android?

Use enum when you need its features. Don't avoid it strictly.

Java enum is more powerful, but if you don't need its features, use constants, they occupy less space and they can be primitive itself.

When to use enum:

  • type checking - you can accept only listed values, and they are not continuous (see below what I call continuous here)
  • method overloading - every enum constant has its own implementation of a method

    public enum UnitConverter{
    METERS{
    @Override
    public double toMiles(final double meters){
    return meters * 0.00062137D;
    }

    @Override
    public double toMeters(final double meters){
    return meters;
    }
    },
    MILES{
    @Override
    public double toMiles(final double miles){
    return miles;
    }

    @Override
    public double toMeters(final double miles){
    return miles / 0.00062137D;
    }
    };

    public abstract double toMiles(double unit);
    public abstract double toMeters(double unit);
    }
  • more data - your one constant contains more than one information that cannot be put in one variable

  • complicated data - your constant need methods to operate on the data

When not to use enum:

  • you can accept all values of one type, and your constants contain only these most used
  • you can accept continuous data

    public class Month{
    public static final int JANUARY = 1;
    public static final int FEBRUARY = 2;
    public static final int MARCH = 3;
    ...

    public static String getName(final int month){
    if(month <= 0 || month > 12){
    throw new IllegalArgumentException("Invalid month number: " + month);
    }

    ...
    }
    }
  • for names (like in your example)
  • for everything else that really doesn't need an enum

Enums occupy more space

  • a single reference to an enum constant occupies 4 bytes
  • every enum constant occupies space that is a sum of its fields' sizes aligned to 8 bytes + overhead of the object
  • the enum class itself occupies some space

Constants occupy less space

  • a constant doesn't have a reference so it's a pure data (even if it's a reference, then enum instance would be a reference to another reference)
  • constants may be added to an existing class - it's not necessary to add another class
  • constants may be inlined; it brings extended compile-time features (such as null checking, finding dead code etc.)

Kotlin enum class in Android performance

It would appear so, yes.

I created this in Kotlin:

enum class Thingies {
Red,
Green,
Blue
}

And decompiled it with javap -v, and here is the header:

public final class Thingies extends java.lang.Enum<Thingies>
minor version: 0
major version: 52
flags: ACC_PUBLIC, ACC_FINAL, ACC_SUPER, ACC_ENUM

Bottom line: they are identical, so you probably have to treat them the same way.

Android: enum vs static final ints?

Enum advantages from this question:

  • They are much more type-safe than integers, strings, or sets of boolean
    flags.
  • They lead to more readable code.
  • It's more difficult to set an enum to an invalid value than an int or
    string.
  • They make it easy to discover the allowed values for a variable or
    parameter.
  • Everything I've read indicates that they perform just as well as integers
    in C# and most JVMs.

I would add:

  • Enums can have member and instance variables, whereas an int can't.

Like most abstractions, they are generally unequivocally advantageous once their performance catches up. Especially in your application code (as opposed to framework code) I would choose enums over other methods that simulate them.

Android Performance: Strings vs. Enums vs. Static Final Ints

Okay, so I did some research online and I found information that was either vague, unreliable or contradictory - in short, I didn't find an answer. So instead, I set up a little app to actually test this for myself.

The app passed 200 items in a bundle from one activity to another, then used those values for a mix of comparisons and lookups from arrays and maps. It then repeated this another 9,999 times.

With 10k revolutions of this process, ran several times for each of the three methods, I came to a conclusion:

It doesn't really matter which one you pick.

The differences in performance are tiny, and I mean miniscule. Strings lost the race most of the time by a slim margin, but ints and enums were both top contenders, and sometimes strings beat them both anyway.

The differences in memory consumption were just as narrow. As long as your string values aren't paragraphs of text, the differences are honestly too small to care about. We're talking an increase of a few percent.

Summary:

Sure, if your app is passing around thousands of Bundle values all the time*, then it might be worth optimising it with ints or enums (whichever comes most naturally to your codebase). But otherwise, any increase you get simply isn't worth the effort it would take to create it.

*If you're doing that, you probably know about this already, or there's something wrong in your app's infrastructure!

Working with Enums in android

Where on earth did you find this syntax? Java Enums are very simple, you just specify the values.

public enum Gender {
MALE,
FEMALE
}

If you want them to be more complex, you can add values to them like this.

public enum Gender {
MALE("Male", 0),
FEMALE("Female", 1);

private String stringValue;
private int intValue;
private Gender(String toString, int value) {
stringValue = toString;
intValue = value;
}

@Override
public String toString() {
return stringValue;
}
}

Then to use the enum, you would do something like this:

Gender me = Gender.MALE

Does proguard converts all enums to int or needs to be configured for this

The optimization is listed on ProGuard's optimizations page. It appears to be one of the default optimizations, but it (like other optimizations) can be specified explicitly if you need more control (e.g. disabling all class/* optimizations aside from enum unboxing).

class/unboxing/enum

Simplifies enum types to integer constants, whenever possible.



Related Topics



Leave a reply



Submit