Как я могу получить доступ к нескольким измерениям в одном массиве измерений?

Я получил следующие коды:

Boo[,,] boos = new Boo[8, 8, 8];
Boo GetMe(int i, int j, int k)
{
    return boos[i, j, k];
}

Приведенный выше код неэффективен, поэтому я конвертирую его в одномерный массив:

Boo[] boosone;
Boo[,,] boos = new Boo[8, 8, 8];
Boo GetMe(int i, int j, int k)
{
   if (boosone == null)
   {
       boosone = new Boo[8 * 8 * 8];
       int num = 0;
       for (int x = 0; x < 8; x++)
       {
           for (int y = 0; y < 8; y++)
           {
               for (int z = 0; z < 8; z++)
               {
                   boosone[num] = boos[x, y, z];
                   num++;
               }
           }
       }
    }
    return boosone[?];
}

Как я могу получить Boo (из той же позиции, что и в многомерном массиве jkl) из одномерного массива boosone?

"что неэффективно", что неэффективно и почему неэффективно?

Lasse V. Karlsen 03.03.2019 22:57
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
1
151
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

Не совсем уверен, почему вы говорите, что первый 3D-массив неэффективен (я имею в виду, действительно ли вы заметили особенно сильное замедление при его использовании?), но вы можете сделать это с помощью некоторых простых вычислений смещения.

Прежде всего, если вы ориентируетесь на последнюю версию C#, вы можете заменить всю функцию копирования всего двумя строками, и тогда ваш код будет выглядеть так:

using System;
using System.Runtime.InteropServices;

Boo[] boosone;
Boo[,,] boos = new Boo[8, 8, 8];

Boo GetMe(int i, int j, int k)
{
    if (boosone == null)
    {
        boosone = new Boo[boos.Length];
        MemoryMarshal.CreateSpan(ref boos[0, 0, 0], boosone.Length).CopyTo(boosone);
    }

    return boosone[boos.GetLength(1) * boos.GetLength(2) * i + boos.GetLength(2) * j + k];
}

Если вы по какой-то причине не хотите использовать класс MemoryMarshal, вы также можете использовать LINQ для выравнивания вашего 3D-массива, хотя этот подход гораздо менее эффективен:

boosone = boos.Cast<Boo>().ToArray();

Под неэффективностью я подразумеваю сильное замедление.

user10311801 03.03.2019 21:58

Я понимаю, что 3D-массивы могут иметь некоторые дополнительные накладные расходы для получения целевых смещений, но общая сложность алгоритма все равно остается прежней. Но если вам нужен линейный массив, то этот код должен отлично справляться с этой задачей, а также избавляться от тех трех циклов, которые вы использовали для копирования массива в одномерный.

Sergio0694 03.03.2019 22:01

Этот код выдает исключение, если вы это сделаете GetMe(7, 7, 7);IndexOutOfRangeException

user10311801 04.03.2019 01:46

@PiotrKFtw - Насколько велики ваши массивы, чтобы они замедляли работу?

Enigmativity 04.03.2019 02:12

@PiotrKFtw Извините, я опечатался в индексах в последней строке, теперь я это исправил.

Sergio0694 04.03.2019 02:47

Я использую их в больших циклах, и цикл занимает 300-400 мс и тратит впустую производительность процессора для завершения при использовании многомерного массива, в то время как без его использования для завершения требуется 80 мс и намного меньше производительности процессора.

user10311801 04.03.2019 07:17

@PiotrKFtw Опять же, я действительно не понимаю, почему это так, но, тем не менее, мой ответ по-прежнему действителен для вашего вопроса, поскольку он делает именно то, что вы просили. Почему вы удалили отметку как действительную?

Sergio0694 04.03.2019 10:20
int index = (8 * 8 * i) + (8 * j) + k;
return boosone[index];

Доступ к многомерному массиву не медленнее, чем доступ к одномерному массиву, на самом деле они оба хранятся в памяти точно так же. Дело не в том, что вы делаете, а в том, как вы это делаете.

Если вы хотите обернуть какой-либо массив тривиальным методом, дайте компилятору подсказку, что он может быть встроенным

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Boo GetMe(int i, int j, int k)
{
    return boos[i, j, k];
}

Сказав это, этот метод абсолютно ничего не делает и не имеет никаких преимуществ, чем просто использование индексатора массива.

Если вы хотите работать с сегментами массива без накладных расходов на перераспределение, рассмотрите возможность использования Span<T> или Memory<T> или ArraySegment

Примерно в этот момент я бы написал пример кода, однако, поскольку я понятия не имею, что вы делаете, трудно догадаться, что вам нужно.

Я предлагаю скачать БенчмаркДотнет и начать профилировать свой код, чтобы выяснить, какой из них является наиболее эффективным и производительным способом сделать то, что вы хотите, не гадайте...

Ответ принят как подходящий

Почему бы вам не взглянуть на зубчатые массивы, которые обеспечивают лучшую производительность? Я сделал тест (в конфигурации RELEASE), который показал, что ваша оболочка в два раза быстрее, чем массив d3, но jagged в 3 раза быстрее, чем массив d3.

using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;

namespace ArrayWrapper
{
    class ArrayPerformanceTest
    {
        int xSize = 2;
        int ySize = 3;
        int zSize = 4;
        int count = 100000000;
        int delay = 500;

        static void Main(string[] args)
        {
            new ArrayPerformanceTest().Run();
        }

        private void Run()
        {

            var d3Array = CreateD3Array();
            var wrapped = GetD1Adapter(d3Array);
            var jagged = GetJaggedArray(d3Array);

            Thread.Sleep(delay);
            TestD3Array(d3Array);
            Thread.Sleep(delay);
            TestWrappedArray(wrapped);
            Thread.Sleep(delay);
            TestJaggeddArray(jagged);
            Thread.Sleep(delay);
        }

        private int[,,] CreateD3Array()
        {
            var rectangular = new int[xSize, ySize, zSize];

            int i = 7;
            for (var x = 0; x < xSize; x++)
                for (var y = 0; y < ySize; y++)
                    for (var z = 0; z < zSize; z++)
                        rectangular[x, y, z] = ++i;

            return rectangular;
        }

        private int[] GetD1Adapter(int[,,] d3Array)
        {
            return d3Array.Cast<int>().ToArray();
        }

        private int[][][] GetJaggedArray(int[,,] d3Array)
        {
            var xSize = d3Array.GetUpperBound(0) + 1;
            var ySize = d3Array.GetUpperBound(1) + 1;
            var zSize = d3Array.GetUpperBound(2) + 1;

            var jagged = new int[xSize].Select(j => new int[ySize].Select(k => new int[zSize].ToArray()).ToArray()).ToArray();

            for (var x = 0; x < xSize; x++)
                for (var y = 0; y < ySize; y++)
                    for (var z = 0; z < zSize; z++)
                        jagged[x][y][z] = d3Array[x, y, z];

            return jagged;
        }

        private void TestD3Array(int[,,] d3Array)
        {
            int i;
            var sw = new Stopwatch();
            sw.Start();
            for (var c = 0; c < count; c++)
                for (var x = 0; x < xSize; x++)
                    for (var y = 0; y < ySize; y++)
                        for (var z = 0; z < zSize; z++)
                            i = d3Array[x, y, z];
            sw.Stop();
            Console.WriteLine($"{nameof(d3Array),7} {sw.ElapsedTicks,10}");
        }

        private void TestWrappedArray(int[] wrapped)
        {
            int i;
            var sw = new Stopwatch();
            sw.Start();
            for (var c = 0; c < count; c++)
                for (var x = 0; x < xSize; x++)
                    for (var y = 0; y < ySize; y++)
                        for (var z = 0; z < zSize; z++)
                            i = wrapped[x * ySize * zSize + y * zSize + z];
            sw.Stop();
            Console.WriteLine($"{nameof(wrapped),7} {sw.ElapsedTicks,10}");
        }

        private void TestJaggeddArray(int[][][] jagged)
        {
            int i;
            var sw = new Stopwatch();
            sw.Start();
            for (var c = 0; c < count; c++)
                for (var x = 0; x < xSize; x++)
                    for (var y = 0; y < ySize; y++)
                        for (var z = 0; z < zSize; z++)
                            i = jagged[x][y][z];
            sw.Stop();
            Console.WriteLine($"{nameof(jagged),7} {sw.ElapsedTicks,10}");
        }
    }
}

Выход:

d3Array   15541709
wrapped    8213316
 jagged    5322008

Я также проанализировал использование процессора.

Она одинакова для всех 3-х подходов.

Важна не только скорость, но и мощность процессора.

user10311801 04.03.2019 19:42

Я провел дополнительное тестирование. Загрузка ЦП в порядке для зубчатого массива. См. правки к ответу.

Alex 05.03.2019 11:40

Другие вопросы по теме