5

I want to initiate a list of N objects with zeros( 0.0 ) . I thought of doing it like that:

var TempList = new List<float>(new float[(int)(N)]);

Is there any better(more efficeint) way to do that?

axcelenator
  • 1,497
  • 3
  • 18
  • 42

3 Answers3

7

Your current solution creates an array with the sole purpose of initialising a list with zeros, and then throws that array away. This might appear to be not efficient. However, as we shall see, it is in fact very efficient!

Here's a method that doesn't create an intermediary array:

int n = 100;

var list = new List<float>(n);

for (int i = 0; i < n; ++i)
    list.Add(0f);

Alternatively, you can use Enumerable.Repeat() to provide 0f "n" times, like so:

var list = new List<float>(n);
list.AddRange(Enumerable.Repeat(0f, n));

But both these methods turn out to be a slower!

Here's a little test app to do some timings.

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace Demo
{
    public class Program
    {
        private static void Main()
        {
            var sw = new Stopwatch();

            int n = 1024*1024*16;
            int count = 10;
            int dummy = 0;

            for (int trial = 0; trial < 4; ++trial)
            {
                sw.Restart();

                for (int i = 0; i < count; ++i)
                    dummy += method1(n).Count;

                Console.WriteLine("Enumerable.Repeat() took " + sw.Elapsed);
                sw.Restart();

                for (int i = 0; i < count; ++i)
                    dummy += method2(n).Count;

                Console.WriteLine("list.Add() took " + sw.Elapsed);
                sw.Restart();

                for (int i = 0; i < count; ++i)
                    dummy += method3(n).Count;

                Console.WriteLine("(new float[n]) took " + sw.Elapsed);

                Console.WriteLine("\n");
            }
        }

        private static List<float> method1(int n)
        {
            var list = new List<float>(n);
            list.AddRange(Enumerable.Repeat(0f, n));
            return list;
        }

        private static List<float> method2(int n)
        {
            var list = new List<float>(n);

            for (int i = 0; i < n; ++i)
                list.Add(0f);

            return list;
        }

        private static List<float> method3(int n)
        {
            return new List<float>(new float[n]);
        }
    }
}

Here's my results for a RELEASE build:

Enumerable.Repeat() took 00:00:02.9508207
list.Add() took 00:00:01.1986594
(new float[n]) took 00:00:00.5318123

So it turns out that creating an intermediary array is quite a lot faster. However, be aware that this testing code is flawed because it doesn't account for garbage collection overhead caused by allocating the intermediary array (which is very hard to time properly).

Finally, there is a REALLY EVIL, NASTY way you can optimise this using reflection. But this is brittle, will probably fail to work in the future, and should never, ever be used in production code.

I present it here only as a curiosity:

private static List<float> method4(int n)
{
    var list = new List<float>(n);
    list.GetType().GetField("_size", BindingFlags.NonPublic | BindingFlags.Instance).SetValue(list, n);
    return list;
}

Doing this reduces the time to less than a tenth of a second, compared to the next fastest method which takes half a second. But don't do it.

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • It's not inefficient to create an array in this way: `new float[1000]`. The `List` constructor will check if the passed `IEnumerable` can be casted to a collection. Then it uses `CopyTo`. http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,d2ac2c19c9cf1d44,references – Tim Schmelter Jul 16 '15 at 13:12
  • @TimSchmelter It's pretty inefficient to create an array on the heap, initialise it to all zeros, copy that array to another array, and then let the garbage collector reclaim the original array from the heap. Memory allocations are relatively expensive. – Matthew Watson Jul 16 '15 at 13:13
  • 1
    Point taken, it might be more efficient than the constructor. At least if the list is very large or must be created very often in a short time due to GC issues. But `Enumerable.Repeat` is not a good alternative because then the list doesn't know the size, so has to enumerate the sequence and perhaps to resize the internal array often. – Tim Schmelter Jul 16 '15 at 13:21
  • 1
    Good point about the `Enumerable.Repeat()`! Just for interest's sake, I'm making some timings. :) – Matthew Watson Jul 16 '15 at 13:25
  • [My own question here](http://stackoverflow.com/questions/10519275/high-memory-consumption-with-enumerable-range) is about `Enumerable.Range`, but the same applies to `Enumerable.Repeat`. – Tim Schmelter Jul 16 '15 at 13:29
  • 1
    @TimSchmelter Turns out you were right anyway - the OP code using the intermediary array is fastest, it seems. I changed my answer accordingly! I also incorporated your suggestion for Enumerable.Range(), but it made little difference. – Matthew Watson Jul 16 '15 at 13:35
  • Thanks for your time! – axcelenator Jul 16 '15 at 13:40
0

What is wrong with

float[] A = new float[N];

or

List<float> A = new List<float>(N);

Note that trying to micromanage the compiler is not optimization. Start with the cleanest code that does what you want and let the compiler do its thing.

Edit 1 The solution with List<float> produces an empty list, with only internally N items initialized. So we can trick it with some reflection

    static void Main(string[] args)
    {
        int N=100;

        float[] array = new float[N];

        List<float> list=new List<float>(N);

        var size=typeof(List<float>).GetField("_size", BindingFlags.Instance|BindingFlags.NonPublic);
        size.SetValue(list, N);

        // Now list has 100 zero items
    }
John Alexiou
  • 28,472
  • 11
  • 77
  • 133
0

Why not:

var itemsWithZeros = new float[length];
Elisabeth
  • 20,496
  • 52
  • 200
  • 321