Страницы: 1 2 След.
RSS
Как правильней, обращаться к границам массива внутри цикла через UBound и LBound или до цикла записать их в переменные?
 
Здравия, уважаемые форумчане.
Думаю, вопрос достаточно полно освещён в названии темы.
Немного проиллюстрирую его.
Допустим у нас имеются два одномерных массива и мы как-то сравниваем/сопоставляем элементы одного массива с другим.
Какой из следующих подходов будет более верным?
ПЕРВЫЙ
Код
    For i1 = LBound(arr1, 1) To UBound(arr1, 1)
        For i2 = LBound(arr2, 1) To UBound(arr2, 1)
            ' что-то тут делаем
        Next i2
    Next i1
или ВТОРОЙ
Код
    i2l = LBound(arr2, 1)
    i2u = UBound(arr2, 1)
    For i1 = LBound(arr1, 1) To UBound(arr1, 1)
        For i2 = i2l To i2u
            ' что-то тут делаем
        Next i2
    Next i1
Спасибо.
P.S. Как правило я предпочитал использовать второй вариант, но сегодня усомнился в его эффективности...

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
Ввозможно в некоторых случаях подойдет ТРЕТИЙ вариант
Код
    For Each item1 In arr1
        For Each item2 In arr2
            ' что-то тут делаем
        Next
    Next 

но не всегда, ну а что касается эффективности второго, так почему сомнения?  Точнее не так. Наверно ожидается проявление эффекта при больших размерностях первого массива.
По вопросам из тем форума, личку не читаю.
 
Где-то читал что сложность алгоритма функции UBound - О(1), не найду ссылку. И на практике не значительно проигрывает переменным (нужно читать подробнее матчасть, возможно расположение массива в куче и его индексов границ, а простые переменные на стеке работают несколько быстрее и и.л.). Когда тестировал сильной не замечал разницы. JayBhagavan,  вот вы протестируйте к примеру на миллионе итераций скорость локальной переменной и UBound(). И нам расскажите)
Изменено: bedvit - 28.10.2018 09:12:15
«Бритва Оккама» или «Принцип Калашникова»?
 
БМВ, спасибо за Ваш ответ. Но я бы хотел именно определиться по данной теме.
bedvit, спасибо за Ваш ответ. Мне такие тонкости про UBound и LBound не встречались. Тестирование - это решение в лоб, а я бы именно хотел понять как лучше и почему.

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
Моё мнение: если предполагается многократное использование значения UBound, то, конечно, лучше сначала присвоить его переменной. А вот применительно к LBound этого делать смысла нет: мы ведь всегда знаем - нолик там или единичка )
 
Цитата
Юрий М написал:
мы ведь всегда знаем - нолик там или единичка
Отнюдь нет, массив может быть передан в процедуру как параметр, и может иметь любой LBound. И любое число размерностей.
UBound, действительно, очень быстрая функция. Обращение к ней требует меньше действий, чем обращение к элементу массива, т.к. при обращении к элементу массива производится проверка, находится ли индекс в пределах LBound и UBound. По каждой размерности. Цикл For Each по элементам массива работает быстрее, чем цикл по индексам, потому что там такая проверка не производится.
Изменено: Казанский - 28.10.2018 11:19:03
 
Ну тогда аналогично с UBound.
Хотя... Алексей, но мы ведь знаем, КАКОЙ массив передаём?
 
JayBhagavan,  Если память не подводит, и bedvit, подправит или подтвердит, то в С нет аналога U(L)bound . Если так, то размерность хранится в укромном месте и разница извлечения не сильно должны отличаться.
По вопросам из тем форума, личку не читаю.
 
Доброго здоровья всем!
На счет UBound - проверял лично на миллионах циклов. Нет ни какой разницы, использовать UBound или переменную, а которую он записан. Единственный выигрыш - переменную, как правило, писать быстрее, чем UBound(a) При выполнении кода - разницы нет.
LBound не проверял, но, скорее вего аналогично.
 
Доброе время суток
Цитата
Михаил С. написал:
Нет ни какой разницы,
Михаил, можно пример тестового кода?
 
Цитата
JayBhagavan написал:
Тестирование - это решение в лоб, а я бы именно хотел понять как лучше и почему.
все два способа корректны, а как лучше и почему для каждого разные понятие... Лучше для чего, для скорости, для краткости записи кода и т.д.? "как лучше" мне, возможно не подойдет "как лучше" для вас. Решение в лоб иногда самое эффективное, для понимание "что лучше" для вас. Я обычно использую UBound().
Цитата
Юрий М написал:
многократное использование значения UBound, то, конечно, лучше сначала присвоить его переменной
Юрий, а почему?:) Размер массива (индексы), по-моему сохраняются в момент формирования массива или изменения его размеров, функция UBound() просто "достает" этот сохраненный индекс в "контейнере" VBA - массив.
К примеру есть свойство массива Array.Length Property (в VB.NET)-Получение значения данного свойства является операцией порядка сложности O(1)., которое хранит размер массива (по сути, та-же  переменная). Матчасть сейчас не найду, но помоему VBA-массивы могут быть похожими по реализации.
Массивы VB.
«Бритва Оккама» или «Принцип Калашникова»?
 
Цитата
bedvit написал:
Юрий, а почему?
В теории не силён, но считал, что UBound(Arr) каждый раз пересчитывается. Понимаю, что разговор про микросекунды, но всё же )
 
Ого! сколько написали, пока я отвечал. Михаил С., и по моим замерам разница очень незначительная. Делал реализацию длинной арифметики (VBA), где это очень  критично, делала на UBound().
Андрей, если найду сброшу, давно это делал. Может Михаил найдет быстрее.
БМВ, нет такой функции в Си, но есть sizeof()
в С++, в STL-контейнерах это просто свойство size()
«Бритва Оккама» или «Принцип Калашникова»?
 
Цитата
Казанский написал:
Цикл For Each по элементам массива работает быстрее, чем цикл по индексам, потому что там такая проверка не производится.
а вот это уже интересно, Казанский, есть ссылка почитать и замеры по скорости?
Цитата
Юрий М написал:
UBound(Arr) каждый раз пересчитывается.
в том и дело, что может не пересчитываться, а храниться в "контейнере" массив VBA как свойство (очень похоже, но исходников реализации VBA массивов не видел, могу ошибаться). Казанский,  у вас нет подобной информации?
Изменено: bedvit - 28.10.2018 13:13:32
«Бритва Оккама» или «Принцип Калашникова»?
 
Цитата
Казанский написал:
Цикл For Each по элементам массива работает быстрее, чем цикл по индексам
А вот с этим пришлось самому столкнуться: перебирал по индексам элементы большой коллекции - с каждой итерацией скорость падала. По совету ZVI сделал перебор For Each - небо и земля.
 
Не думал, что тема станет настолько оживлённой.
Из справки майкрософт не понятно, вычисляется ли верхняя и нижняя границы массива либо хранятся в какой-то спец. области выделенной под массив. Это меня и интересует в первую очередь. (было бы не плохо получить ссылку на почитать об этом...)
Всем спасибо за участие.
Замечание по "форычу" приму к сведению.

Формула массива (ФМ) вводится Ctrl+Shift+Enter
Memento mori
 
Цитата
bedvit написал:
UBound(Arr) каждый раз пересчитывается... - в том и дело, что может не пересчитываться, а храниться в "контейнере"
Если речь о цикле For, то в нем параметры цикла (начальное значение, конечное значение, шаг) вычисляются ОДИН раз и хранятся в стеке переменных процедуры. В циклах Do и While условие выхода из цикла вычисляется каждый раз. Убедиться можно так
Код
Sub bb()
Dim i
Debug.Print "For ---"
  For i = A1 To A2 Step A3
  Next
Debug.Print "Do ---"
  i = 0
  Do
    i = i + 1
  Loop Until i >= A3
Debug.Print "While ---"
  i = 0
  While i < A3
    i = i + 1
  Wend
End Sub

Function A1()
  Debug.Print "A1"
  A1 = 1
End Function

Function A2()
  Debug.Print "A2"
  A2 = 10
End Function

Function A3()
  Debug.Print "A3"
  A3 = 3
End Function
 
Цитата
Андрей VG написал:
Михаил, можно пример тестового кода?
Я счас с андроида,пример кода не могу, но, учитывая #17, вполне возможно, что алгоритм не совсем верен.
 
Цитата
bedvit написал:
может не пересчитываться, а храниться в "контейнере"
Код
  For i1 = LBound(arr1, 1) To UBound(arr1, 1)
        For i2 = LBound(arr2, 1) To UBound(arr2, 1)

UBound(arr1, 1) вычисляться 1 раз.
A сколько раз вычисляется UBound(arr2, 1)?
 
RAN, насколько я понял из обсуждения - ни разу. Эти значения хранятся так же, как и значения переменных. ИМХО
 
Казанский, RAN, как всегда наглядный и ясный ответ, но я немного о другом, наверное не очень понятно пояснил. Я вот о чем...RAN, наверное Михаил С. меня понял.
Дело в следующем: давайте вспомним, есть просты типы данных и сложные (составные), к коим и относятся массивы, списки, словари, другие конструкции. Динамический массив может быть реализован в виде класса (ООП) или шаблона/структуры/контейнера. Структура реализации этого типа данных может различаться (реализация VBA-массива я не знаю и с удовольствием глянул бы). К примеру в С++ популярен Vector, есть открытые исходники и описание структуры.
Не привязываясь к языкам, выложу момент к которому я подвожу:
Цитата
Наиболее распространённой для типичных процедурных компилируемых языков является реализация изменения размера массива путём перемещения его в динамической памяти.
Под массив выделяется фрагмент ОЗУ, размер которого больше требуемого т. н. логического размера (size или length). Количество элементов, которое фактически может быть размещено в этой памяти, называется ёмкостью (capacity) динамического массива. Текущая длина массива хранится в отдельном счётчике. Она может использоваться для определения текущего размера, а также для контроля выхода за границы массива, если язык поддерживает такой контроль.
Источник цитаты. Подозреваю, что и в массивах VBA, есть счетчик который хранит размер массива, и его то и забирает UBound(), не пересчитывая никаких значений.
«Бритва Оккама» или «Принцип Калашникова»?
 
Цитата
Юрий М написал:
считал, что UBound(Arr) каждый раз пересчитывается.
Проверяем.
Код
Sub test1()
  Dim arr, i As Long
  ReDim arr(1 To 5)
  For i = 1 To UBound(arr)
    Debug.Print i
    If i = 3 Then
      ReDim arr(1 To 10)
    End If
  Next i
  Debug.Print "Ubound", UBound(arr)
End Sub
Не пересчитывается. Попробуем запутать компилятор с циклом For Each.
Код
Sub test2()
  Dim arr, v, i As Long
  ReDim arr(1 To 5) As Long
  For Each v In arr
    Debug.Print i, v
    i = i + 1
    If i = 3 Then
      ReDim Preserve arr(1 To 10)
    End If
  Next v
  Debug.Print "Ubound", UBound(arr)
End Sub

Не дает выполнить второй Redim, так как массив Arr "временно заблокирован".
Массивы VBA хранятся как структуры SAFEARRAY, в которых, в частности, есть информация о размерностях массива.
Разница между подходами 1 и 2 в #1 вряд ли будет ощутима на современных конфигурациях и их применение - дело вкуса.
Изменено: sokol92 - 28.10.2018 16:42:14
Владимир
 
Цитата
sokol92 написал:
Разница между подходами 1 и 2 в #1 вряд ли будет ощутима на современных конфигурациях
Добрый вечер, Владимир. У меня получилось почти 10% отношение.
Код
Public Sub testUBound()
    Const itCount As Long = 100000000
    Dim arr() As Long, lastId As Long
    Dim i As Long, vSum As Long
    Dim t1 As Single, t2 As Single, id As Long
    ReDim arr(1 To 1)
    'with ubound test
    t1 = Timer
    For i = 1 To itCount
        For id = 1 To UBound(arr, 1)
            vSum = vSum + arr(id)
        Next
    Next
    t1 = Timer - t1
    
    'without ubound test
    lastId = UBound(arr, 1)
    t2 = Timer
    For i = 1 To itCount
        For id = 1 To lastId
            vSum = vSum + arr(id)
        Next
    Next
    t2 = Timer - t2
    Debug.Print "difference with and without ubound " & CStr(t1 - t2)
    Debug.Print "without time length " & CStr(t2)
End Sub
Всё же вызов функции имеет дополнительные затраты. Другое дело, что такого примитива в вычислениях, как правило, в циклах не производится - именно поэтому не видно разницы.
 
Здравствуйте, Андрей! Спасибо за пример. Согласились, что вычисление границ массива 100 млн. раз - это все же "не типично" (© С.Огурцов) :)  
Владимир
 
Всем добрый вечер. Интересная тема . Уточняющий вопрос. Использовать for each в двухмерном массыве будет сложно для выборки конкреного поля или есть какие-то методы?
Изменено: ivanok_v2 - 28.10.2018 18:23:45
 
Перебор элементов любого большого массива циклом For Each быстрее, чем с помощью цикла (циклов для многомерных) по индексу (индексам). С другой стороны, не всё можно сделать, не зная индексов.
Владимир
 
Цитата
sokol92 написал:
Массивы VBA хранятся как структуры  SAFEARRAY , в которых, в частности, есть информация о размерностях массива.
Владимир спасибо за матчасть, тогда функция UBound() просто указатель/ссылка на поле rgsabound (с указанием размерности), что в итоге как я и предполагал является хранимой величиной, и от переменной мало чем отличается, разве тем, что в первом способе хранится у нашей переменной, а во втором в свойстве/массиве rgsabound[ ] структуры SAFEARRAY. Андрей, 10% это серьезно, у меня получилось на уровне погрешности. Все же локальная переменная на стеке быстрее чем свойство массива в куче (предполагаю что так организован доступ к памяти в VBA), но 10%, хм...
Цитата
sokol92 написал:
Перебор элементов любого большого массива циклом For Each быстрее, чем с помощью цикла (циклов для многомерных) по индексу (индексам).
Владимир, есть тест?
Изменено: bedvit - 28.10.2018 19:39:03
«Бритва Оккама» или «Принцип Калашникова»?
 
Цитата
bedvit написал:
10% это серьезно
Добрый вечер, Виталий.
Ситуация уж больно искусственная, на это обратил внимание Владимир, сто миллионов вызовов UBound сложно обеспечить в реальном коде, плюс, крайне редко в коде бывает, что бы в теле цикла была только такая простая операция, как накопительное суммирование. Что-то чуть по сложнее и разница по времени будет не принципиальна.
Цитата
bedvit написал:
циклов для многомерных
Вот тут частично да, частично нет. Переменная для For Each должна быть в VBA типа Variant, а он весьма тормознутый. Тестовый пример,
Скрытый текст
Обычный For по двумерному массиву почти в два раза быстрее на моём нетбуке (2,17 секунды против 3,78).
А вот что обидно в VB.NET For Each безумно безумно долго :(  Причина не понятна от слова совсем
for in for 00:00:00.6258609
for each 00:00:13.4449526
Код
Скрытый текст

По идее же For Each должен быть как адрес чтения ТекущаяПозиция + РазмерЭлемента, против (для одномерного случая). НачалоМассива + Индекс * РазмерЭлемента.
 
Цитата
bedvit написал:
Владимир, есть тест?
Здравствуйте, Виталий! В дополнение к "продвинутым" тестам Андрея самый примитивный
Код
Sub test()
  Dim n As Long, i As Long, j As Long, arr(), t As Double, v, v2
  n = 10000 ' размерность двумерного массива
  ReDim arr(1 To n, 1 To n)
  
  t = Timer
  For Each v In arr
    v2 = v
  Next v
  Debug.Print "For Each", Timer - t
  
  t = Timer
  For i = 1 To n
    For j = 1 To n
      v2 = arr(i, j)
    Next j
  Next i
  Debug.Print "For i=1", Timer - t
End Sub

На современной конфигурации (Win10 Excel 2016(64-разрядный)) цикл For each быстрее в 1,5 раза. На старой конфигурации (Win7 Excel 2007) при n=5000 цикл For Each быстрее на 15%. При малых значениях n доступ с перебором индексов даже чуть-чуть быстрее.
Владимир
 
Цитата
sokol92 написал:
"продвинутым" тестам
Владимир, не перебарщивайте. Тестовый пример точно такой-же. Типизировал и ввёл "осмысленную" операцию - сумму членов массива.
Скрытый текст

Результат для n: 5000 и 10000
Цитата
For Each       1,6015625
For i=1        1,296875
For Each       6,4140625
For i=1        5,5546875
Всё не так однозначно.
P. S. А если задать последовательный обход элементов массива по памяти в обычном For, то и ещё интереснее получается
Скрытый текст

Цитата
For Each       6,34375
For i=1        2,8359375  
Порядок - он всегда имеет значение :)
Изменено: Андрей VG - 28.10.2018 22:58:13
Страницы: 1 2 След.
Наверх