Инициировать список с плавающей точкой с нулями в C#

Я хочу, чтобы начать список N объектов с нулями ( 0.0) . Я думал сделать это так:

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

Есть ли лучший (более эффективный) способ сделать это?

3 ответа

Решение

Ваше текущее решение создает массив с единственной целью - инициализировать список нулями, а затем выбрасывает этот массив. Это может показаться неэффективным. Однако, как мы увидим, это действительно очень эффективно!

Вот метод, который не создает промежуточный массив:

int n = 100;

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

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

Кроме того, вы можете использовать Enumerable.Repeat() предоставлять 0f "п" раз, вот так:

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

Но оба эти метода оказываются медленнее!

Вот небольшое тестовое приложение, чтобы сделать некоторые тайминги.

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]);
        }
    }
}

Вот мои результаты для сборки RELEASE:

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

Так что получается, что создание промежуточного массива происходит намного быстрее. Однако следует помнить, что этот код тестирования имеет недостатки, поскольку он не учитывает накладные расходы на сборку мусора, вызванные выделением промежуточного массива (который очень трудно правильно определить).

Наконец, есть ДЕЙСТВИТЕЛЬНО ЗЛО, ПРОТИВНО, как вы можете оптимизировать это с помощью отражения. Но это хрупко, вероятно, не сработает в будущем и никогда не должно использоваться в производственном коде.

Я представляю это здесь только как любопытство:

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;
}

Это сокращает время до менее одной десятой секунды по сравнению со следующим самым быстрым методом, который занимает полсекунды. Но не делай этого.

Почему бы и нет:

var itemsWithZeros = new float[length];

Что не так с

float[] A = new float[N];

или же

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

Обратите внимание, что попытка микроуправления компилятором не является оптимизацией. Начните с самого чистого кода, который делает то, что вы хотите, и пусть компилятор сделает свое дело.

Редактировать 1 Решение с List<float> создает пустой список, только с внутренним N элементы инициализированы. Таким образом, мы можем обмануть это с некоторым отражением

    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
    }
Другие вопросы по тегам