Java: многомерный массив по сравнению с одномерным

substr - очень удобная базовая функция R:

a[substr(a, 1, 1) %in% c("M", "m")]

# [1] "Mom"    "mother"

И поскольку вы упомянули sub(), то вы могли бы сделать (хотя и не обязательно рекомендуется):

a[sub("(.).*", "\\1", a) %in% c("M", "m")]
27
задан 0xCursor 27 October 2019 в 01:49
поделиться

4 ответа

Обычно лучше всего искать ответы на такие вопросы, чтобы увидеть, как варианты компилируются в байт-код JVM:

multi = new int[50][50];
single = new int[2500];

Это переводится на:

BIPUSH 50
BIPUSH 50
MULTIANEWARRAY int[][] 2
ASTORE 1
SIPUSH 2500
NEWARRAY T_INT
ASTORE 2

Итак, как вы можете видеть, JVM уже знает, что мы говорим о многомерном массиве.

Продолжая:

for (int i = 0; i < 50; ++i)
    for (int j = 0; j < 50; ++j)
    {
        multi[i][j] = 20;
        single[i*50+j] = 20;
    }

Это переводится (пропуская циклы) в:

ALOAD 1: multi
ILOAD 3: i
AALOAD
ILOAD 4: j
BIPUSH 20
IASTORE

ALOAD 2: single
ILOAD 3: i
BIPUSH 50
IMUL
ILOAD 4: j
IADD
BIPUSH 20
IASTORE

Итак, как вы можете видеть, многомерный массив обрабатывается внутренне в виртуальной машине нет служебных данных, генерируемых бесполезными инструкциями, в то время как при использовании одной команды используется больше команд, поскольку смещение вычисляется вручную.

Я не думаю, что производительность будет такой проблемой.

РЕДАКТИРОВАТЬ:

Я сделал несколько простых тестов, чтобы увидеть, что здесь происходит. Я решил попробовать разные примеры: линейное чтение, линейная запись и произвольный доступ. Время выражается в миллисекундах (и рассчитывается с использованием System.nanoTime(). Вот результаты:

Линейная запись

  • Размер: 100x100 (10000) Мульти: 5.786591 Одиночный: 6.131748
  • Размер: 200x200 (40000) Мульти: 1.216366 Одноместный: 0,782041
  • Размер: 500x500 (250000) Мульти: 7.177029 Одноместный: 3.667017
  • Размер: 1000x1000 (1000000) Мульти: 30.508131 Одноместный: 18.064592
  • Размер: 2000x2000 (4000000) Мульти: 185.3548 Одноместный: 155.590313
  • Размер: 5000x5000 (25000000) Мульти: 955.5299 Одноместный: 923.264417
  • Размер: 10000x10000 (100000000) Мульти : 4084.798753 Одиночный: 4015.448829

Линейное чтение

  • Размер: 100x100 (10000) Мульти: 5.241338 Одноместный: 5.135957
  • Размер: 200x200 (40000) Мульти : 0.080209 Одноместный: 0.044371
  • Размер: 500x500 (250000) Мульти: 0.088742 Одноместный: 0.084476
  • Размер: 1000x1000 (1000000) Мульти: 0.232095 Одноместный: 0.167671
  • Размер: 2000x2000 (4000000) Мульти: 0,481683 Одноместный: 0,33321
  • [1 118] Размер: 5000x5000 (25000000) Мульти: 1.222339 Один: 0.828118 Размер: 10000x10000 (100000000) Мульти: 2.496302 Один: 1.650691

Случайное чтение

  • Размер: 100x100 ( 10000) Multi: 22,317393 Single: 8.546134
  • Размер: 200x200 (40000) Multi: 32.287669 Single: 11.022383
  • Размер: 500x500 (250000) Multi: 189.542751 Single: 68.181343
  • Размер: 1000x1000 (1000000) Мульти: 1124.78609 Одноместный: 272.235584
  • Размер: 2000x2000 (4000000) Мульти: 6814.477101 Одноместный: 1091.998395
  • Размер: 5000x5000 (25000000) Мульти: 50051.306239 Один: 7028.422262 ]

Случайное число немного вводит в заблуждение, поскольку оно генерирует 2 случайных числа для многомерного массива, в то время как только одно для одномерного (и PNRG могут потреблять некоторое количество ресурсов ЦП).

Имейте в виду, что я пытался чтобы позволить JIT работать, измеряя только после 20-го запуска того же цикла. Для полноты картины моя виртуальная машина Java выглядит следующим образом:

Java-версия «1.6.0_17» Java (TM) SE Runtime Environment (сборка 1.6.0_17-b04) Java HotSpot (TM) 64-разрядная виртуальная машина сервера (сборка 14.3-b01, смешанный режим)

68
ответ дан Alexander Stepchkov 28 November 2019 в 04:10
поделиться

Используйте первый вариант (3-мерный), потому что его легче понять и меньше шансов сделать какую-то логическую ошибку (особенно если вы используете его для моделирования 3-мерного пространства)

4
ответ дан Roman 28 November 2019 в 04:10
поделиться

Если вы выберете второй маршрут, то вы ' re придется выполнять арифметические действия для каждого доступа к массиву. Это будет болезненно и подвержено ошибкам (если вы не заключите его в класс, обеспечивающий эту функциональность).

Я не верю, что есть какая-либо (значительная) оптимизация при выборе плоского массива (особенно с учетом арифметики, используемой для индексации в нем). Как всегда при оптимизации, вам нужно будет провести некоторые измерения и определить, действительно ли это того стоит.

2
ответ дан 28 November 2019 в 04:10
поделиться

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

Лично я сначала использовал бы вариант а), потому что это упростит код. Если профилировщик показывает, что доступ к массиву является узким местом, я бы преобразовал его в вариант b), чтобы была пара вспомогательных методов для чтения и записи значений массива (таким образом, беспорядочный код будет ограничен этими двумя методы).

Я провел тест для сравнения 3-мерных массивов int (столбец «Multi») с эквивалентными 1-мерными массивами int (столбец «Single»). Код здесь и тесты здесь . Я запускал его на 64-разрядной версии jdk1.6.0_18, Windows 7 x64, Core 2 Quad Q6600 @ 3,0 ГГц, 4 ГБ DDR2, используя параметры JVM -server -Xmx3G -verbose: gc -XX: + PrintCompilation (Я удалил отладочные данные из следующих результатов). Результаты были следующими:

Out of 20 repeats, the minimum time in milliseconds is reported.

Array dimensions: 100x100x100 (1000000)
            Multi   Single
Seq Write   1       1
Seq Read    1       1
Random Read 99      90    (of which generating random numbers 59 ms)

Array dimensions: 200x200x200 (8000000)
            Multi   Single
Seq Write   14      13
Seq Read    11      8
Random Read 1482    1239    (of which generating random numbers 474 ms)

Array dimensions: 300x300x300 (27000000)
            Multi   Single
Seq Write   53      46
Seq Read    34      24
Random Read 5915    4418    (of which generating random numbers 1557 ms)

Array dimensions: 400x400x400 (64000000)
            Multi   Single
Seq Write   123     111
Seq Read    71      55
Random Read 16326   11144    (of which generating random numbers 3693 ms)

Это показывает, что одномерный массив работает быстрее. Хотя различия настолько малы, что для 99% приложений не будут заметны.

Я также провел несколько измерений, чтобы оценить накладные расходы на генерацию случайных чисел в тесте Random Read, заменив preventOptimizingAway + = array.get (x, y, z); на preventOptimizingAway + = x * y * z; и вручную добавил измерения в приведенную выше таблицу результатов. Генерация случайных чисел занимает 1/3 или меньше общего времени теста произвольного чтения, поэтому доступ к памяти доминирует в тесте, как и ожидалось. Было бы интересно повторить этот тест с массивами из 4 и более измерений. Вероятно, это увеличило бы разницу в скорости, потому что самые верхние уровни многомерного массива поместятся в кэш ЦП, и только другие уровни потребуют поиска в памяти.

22
ответ дан 28 November 2019 в 04:10
поделиться
Другие вопросы по тегам:

Похожие вопросы: