substr
- очень удобная базовая функция R:
a[substr(a, 1, 1) %in% c("M", "m")]
# [1] "Mom" "mother"
И поскольку вы упомянули sub()
, то вы могли бы сделать (хотя и не обязательно рекомендуется):
a[sub("(.).*", "\\1", a) %in% c("M", "m")]
Обычно лучше всего искать ответы на такие вопросы, чтобы увидеть, как варианты компилируются в байт-код 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()
. Вот результаты:
Линейная запись
Линейное чтение
Случайное чтение
Случайное число немного вводит в заблуждение, поскольку оно генерирует 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, смешанный режим)
Используйте первый вариант (3-мерный), потому что его легче понять и меньше шансов сделать какую-то логическую ошибку (особенно если вы используете его для моделирования 3-мерного пространства)
Если вы выберете второй маршрут, то вы ' re придется выполнять арифметические действия для каждого доступа к массиву. Это будет болезненно и подвержено ошибкам (если вы не заключите его в класс, обеспечивающий эту функциональность).
Я не верю, что есть какая-либо (значительная) оптимизация при выборе плоского массива (особенно с учетом арифметики, используемой для индексации в нем). Как всегда при оптимизации, вам нужно будет провести некоторые измерения и определить, действительно ли это того стоит.
На современных процессорах доступ к некэшированной памяти в сотни раз медленнее, чем арифметика (см. эту презентацию и прочтите То, что каждый программист должен знать о памяти ). Вариант а) приведет примерно к 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 и более измерений. Вероятно, это увеличило бы разницу в скорости, потому что самые верхние уровни многомерного массива поместятся в кэш ЦП, и только другие уровни потребуют поиска в памяти.