2017-01-19 3 views
3

Например, для:Какой код создается для цикла Ada Array of Records?

type PERSONCV is 
    record 
     name: String (1..4); 
     age: Integer; 
     cvtext: String (1..2000); 
    end record; 

N: constant := 40000; 
persons : array (1..N) of PERSONCV; 

function jobcandidate return Boolean is 
    iscandidate: Boolean := False; 
begin 
    for p of persons loop -- what code is generated for this? 
     if p.age >= 18 then 
     iscandidate := true; 
     exit; 
     end if; 
    end loop; 
    return iscandidate; 
end; 

В C/C++ часть петли, как правило, будет:

PERSONCV * p; // address pointer 
int k = 0; 
while (k < N) 
    { 
    p = &persons [ k ]; // pointer to k'th record 
    if (p->age >= 18)... 
    ... 
    k++ ; 
    } 

Я прочитал, что Ада использует семантику Value для записей. Выполняет ли цикл Ада над копией k'th для переменной цикла p? , например. как это в C/C++:

PERSONCV p; // object/variable 
int k = 0; 
while (k < N) 
    { 
    memcpy (&p, &persons [ k ], sizeof (PERSONCV)); // copies k'th elem 
    if (p.age >= 18)... 
    ... 
    k++ ; 
    } 
+0

Где вы читали, что Ada использует семантику значения для этого? –

ответ

9

Я подозреваю, что вы читали о Ada неправильно, и, вероятно, хуже, поощряет вас думать о Ada в неправильном направлении.

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

Таким образом, цель состоит в том, чтобы пересечь все Лица, и верните Истину при встрече с первым более 18, иначе верните False.

И все.

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

Затем, вы намерены, что вы просто ожидаете, что компилятор сделает все правильно.

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

Вы можете представить себе процессор с большим количеством регистров, где одна строка в кеше считывает выполнение копии быстрее, чем работает на месте (особенно если нет изменений для записи на содержимое P) или других целевых ЦП где верно обратное. Почему вы хотите, чтобы компилятор не смог добиться лучшей реализации?

Хорошим примером этого является подход Ады для передачи параметров подпрограммам - имя, значение или ссылки семантика действительно не применяются - вместо этого, можно указать параметр режима прохождения - in, out или in out описания потоков информации в (или из) подпрограммы. Интуитивно понятный, обеспечивает семантику, которая может быть более строго проверена, и оставляет компилятор свободным, чтобы выбрать лучшую (самую быструю, наименьшую, в зависимости от вашей цели) реализацию, которая правильно подчиняется этой семантике.

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

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

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

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

+0

Я давно знаю об Аде, но у меня никогда не было возможности ее использовать. До сих пор. Моя Ада пошла ржаво, поэтому я искал походы и наткнулся на несколько должностей (по крайней мере один университетским отделом компьютерных наук), в которых говорилось, что Ада использует ценностную семантику, а не ссылочную семантику. Я обнаружил, что трудно поверить, поскольку это разрушит производительность для обработки больших записей, особенно в циклах. Поэтому я спросил на этом форуме. Я полностью согласен со всем в вашем ответе. Большое спасибо Брайан. – resander

13

Предполагая, что вы используете GNAT, есть два пути исследования.

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

function resander__jobcandidate return boolean is 
     iscandidate : boolean := false; 
     L_1 : label 
    begin 
     L_1 : for C8b in 1 .. 40000 loop 
     p : resander__personcv renames resander__persons (C8b); 
     if p.age >= 18 then 
      iscandidate := true; 
      exit; 
     end if; 
     end loop L_1; 
     return iscandidate; 
    end resander__jobcandidate; 

поэтому вопрос, как же renames переводятся? Учитывая, что размер записи составляет 2008 байт, шансы компилятора, генерирующего копию, в значительной степени равны нулю.

Второй следственный подход заключается в том, чтобы сохранить код сборки, который компилятор обычно испускает ассемблеру, а затем удаляет с помощью переключателя -S. Это подтверждает, что сгенерированный код похож на вашу первую версию C++ (для macOS).

В качестве интересного габаритного фонаря, Ада 2012 допускает альтернативную реализацию jobcandidate:

function jobcandidate2 return Boolean is 
    (for some p of persons => p.age >= 18); 

, который генерирует идентичный код.

+5

Мне нравится альтернативная реализация! –

+2

См. Также [* Обоснование для Ada 2012, §3.4 Количественные выражения *) (http://www.ada-auth.org/standards/12rat/html/Rat12-3-4.html). – trashgod

+0

Варианты GNAT помогли полностью ответить на вопрос. Очень полезно! Большое спасибо Симону. – resander

0

Используя текущего компилятора (GCC 7.0.0), я скопировал ваш источник как Ад-программу и программу C++, используя std:array<char, 4> и т.д., соответствующими String(1..4) и т.д. коммутаторов были просто -O2 для C++, и -O2 -gnatp для Ada , таким образом, чтобы использовать сопоставимые параметры относительно проверен доступ к элементам массива и т.д.

Таковы результаты для jobcandidate:

C++:

movl $_ZN15Loop_Over_Array7personsE+4, %eax 
    movl $_ZN15Loop_Over_Array7personsE+80320004, %edx 
    jmp .L3 
.L8: 
    addq $2008, %rax 
    cmpq %rdx, %rax 
    je .L7 
.L3: 
    cmpl $17, (%rax) 
    jle .L8 
    movl $1, %eax 
    ret 
.L7: 
    xorl %eax, %eax 
    ret 

Ada:

movl $1, %eax 
    jmp .L5 
.L10: 
    addq $1, %rax 
    cmpq $40001, %rax 
    je .L9 
.L5: 
    imulq $2008, %rax, %rdx 
    cmpl $17, loop_over_array__persons-2004(%rdx) 
    jle .L10 
    movl $1, %eax 
    ret 
.L9: 
    xorl %eax, %eax 
    ret 

Одно различие я вижу в том, как реализация использует % EDX и % EAX; для перехода от одного элемента массива к следующему и проверки того, достигнут ли конец. Ада кажется imulq размер элемента для установки курсора, C++ кажется addq его указателю.

Я не оценил производительность.