Initiate a float list with zeros in C#
Asked Answered
R

3

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?

Reconstructive answered 16/7, 2015 at 12:56 Comment(5)
What is inefficient in that approach?Tensiometer
I don't know, I wanted to ask the proffesionals if this is the right way. After initializing that, is there any way to set the list with zeros? (After some use with the list)Reconstructive
How do I set the list with zeros?Reconstructive
0.0 is the default value for a float, so you don't need to perform any additional initialization steps.Palp
If you want efficiency keep it as float[]Primalia
P
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.

Patrimony answered 16/7, 2015 at 13:9 Comment(6)
It's not inefficient to create an array in this way: new float[1000]. The List<T> constructor will check if the passed IEnumerable<T> can be casted to a collection. Then it uses CopyTo. referencesource.microsoft.com/#mscorlib/system/collections/…Tensiometer
@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.Patrimony
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.Tensiometer
Good point about the Enumerable.Repeat()! Just for interest's sake, I'm making some timings. :)Patrimony
My own question here is about Enumerable.Range, but the same applies to Enumerable.Repeat.Tensiometer
@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.Patrimony
P
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
    }
Primalia answered 16/7, 2015 at 13:36 Comment(2)
The second is not equivalent, since it results in a list with Count == 0Patrimony
Oh, yes. Point taken.Primalia
R
0

Why not:

var itemsWithZeros = new float[length];
Remainder answered 13/6, 2016 at 19:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.