Caio Oliveira
Caio Oliveira

Reputation: 151

Android performance issues in populating ArrayList

I've created a method to populate an array based on a Http response I get from a server. I receive the response in a simple format and read it with a Scanner. The response has only around 8000 entries (floats and integers), and it's taking forever to populate the ArrayList, around 1 or 2 minutes. Here is the code

public void update(String str) {
    ProgressDialog pDialog = new ProgressDialog(context);
    pDialog.setMessage("Atualizando pesos, aguarde");
    pDialog.show();
    BufferedReader r = new BufferedReader(new StringReader(str));
    Scanner s = null;
    try{
        s = new Scanner(r);
        ArrayList<ArrayList<ArrayList<Double> > > weights = new ArrayList<ArrayList<ArrayList<Double> > >();
        while(s.hasNextInt()){
            ArrayList<ArrayList<Double> > wl = new ArrayList<ArrayList<Double> >();
            int layerId = s.nextInt(), neuronsAmt = s.nextInt(), inputSize = s.nextInt();
            Log.d("UpdateTask",  "Layer " + layerId + ", neuronios: " + neuronsAmt + ", inputSize: " + inputSize);
            for(int i = 0; i < neuronsAmt; i++){
                ArrayList<Double> wi = new ArrayList<Double>(); 
                for(int j = 0; j < inputSize; j++){
                    wi.add(s.nextDouble());
                }
                wl.add(wi);
            }
            weights.add(wl);
            this.weights = weights;
            if(s.hasNext() && !s.hasNextInt())
                Log.d("UpdateTask", "Depois de tudo tem " + s.next());
        }
    }finally{
        if( s != null) s.close();
        pDialog.dismiss();
    }   
}

I'm calling it from an AsyncTask after the HTTP response is received.

EDIT: I'll try to explain the HTTP structure here. The response actually gives the weights for a neural network. Basically it's an array of matrices (which should remain as matrices, for the evaluation of the neural network to work). The HTTP response is as follows: There are N matrices. Each one starts with an integer (the matrix ID), followed by the number of rows(R) and the number of columns(C) in the matrix. After that, there are R*C floats indicating the value stored in the matrix. Input is terminated when you can't find another layer id.

PS: I wasn't able to make the dialog work either, but that's not a problem for me now.

Upvotes: 0

Views: 177

Answers (3)

Caio Oliveira
Caio Oliveira

Reputation: 151

After the other optimizations discussed here, the results were anything better. The problem is that Scanner is really slow and in Android devices it gets even slower. This is probably due to the amount of buffering it makes in background, I don't know.

I've used a trick from programming contests: tokenizing the input string and parsing the values one by one. The results are great: I can populate the ArrayList in less than a second.

Here is what I got now, working great:

    ...
    String[] tokens = str.trim().split(" ");
    weights = new ArrayList<Double[][]>();
    int begin = 0;
    while(begin < tokens.length){
        int layerId = Integer.parseInt(tokens[begin]), neuronsAmt = Integer.parseInt(tokens[begin+1]), inputSize = Integer.parseInt(tokens[begin+2]);
        begin += 3;
        Double[][] wl = new Double[inputSize][neuronsAmt];
        Log.d("UpdateTask",  "Layer " + layerId + ", neuronios: " + neuronsAmt + ", inputSize: " + inputSize);
        for(int i = 0; i < inputSize; i++){ 
            for(int j = 0; j < neuronsAmt; j++){
                wl[i][j] = Double.valueOf(tokens[begin+i*neuronsAmt+j]);
            }
        }
        begin += inputSize*neuronsAmt;
        weights.add(wl);
    }
    ...

Upvotes: 0

a.ch.
a.ch.

Reputation: 8390

Consider simplifying your array structure, i.e. use casual array where possible. Also initialize ArrayList with capacity, where possible (as leandrocastelli mentioned). For example, your code may be optimized this way:

    ...

    try {
        s = new Scanner(r);
        ArrayList<double[][]> weights = new ArrayList<double[][]>(); // capacity is highly recommended here, even approximate
        while(s.hasNextInt()){

            int layerId = s.nextInt(), neuronsAmt = s.nextInt(), inputSize = s.nextInt();
            double[][] wl = new double[neuronsAmt][]; // just 2D-array of doubles, which is much faster than List of List

            Log.d("UpdateTask",  "Layer " + layerId + ", neuronios: " + neuronsAmt + ", inputSize: " + inputSize);
            for(int i = 0; i < neuronsAmt; i++){
                double[] wi = new double[inputSize]; 
                for(int j = 0; j < inputSize; j++){
                    wi[j] = s.nextDouble();
                }
                wl[i] = wi;
            }
            weights.add(wl);
            this.weights = weights;
            if(s.hasNext() && !s.hasNextInt())
                Log.d("UpdateTask", "Depois de tudo tem " + s.next());
        }
    }
    ...

Upvotes: 1

leandrocastelli
leandrocastelli

Reputation: 546

If you know the amount of entries of your ArrayList (or at least have a clue on that) instatiate it with this capacity. Like this:

 //N is the number of matrices that you are expceting
 ArrayList<ArrayList<ArrayList<Double> > > weights = new ArrayList<ArrayList<ArrayList<Double> > >(N);
  while(s.hasNextInt()){

  ArrayList<ArrayList<Double> > wl = new ArrayList<ArrayList<Double> >(R); 
  int layerId = s.nextInt(), neuronsAmt = s.nextInt(), inputSize = s.nextInt();
  for(int i = 0; i < neuronsAmt; i++){
        ArrayList<Double> wi = new ArrayList<Double>(inputSize); 
            for(int j = 0; j < inputSize; j++){
                wi.add(s.nextDouble());
            }
            wl.add(wi);
  }

Upvotes: 0

Related Questions