2016-02-09 3 views
1

tl; dr: Я обнаружил, что запись «подразумеваемая do» была медленнее, чем явная при определенных обстоятельствах, и хочу понять, почему/если я могу улучшить это.Fortran подразумевает do write speedup

Детали:
У меня есть код, который делает что-то эффект:

DO i=1,n 
    calculations... 

    !m, x, and y all change each pass through the loop 
    IF(m.GT.1)THEN 
    DO j=1,m 
     WRITE(10,*)x(j),y(j) !where 10 is an output file 
    ENDDO 
    ENDIF 
ENDDO 

Выходной файл заканчивает тем, что довольно большой, и поэтому кажется, что написание большой коэффициент производительности, поэтому я хотел его оптимизировать. Прежде чем кто-либо спросит, нет, переход от ASCII не является вариантом из-за различных требований к нисходящему потоку. Соответственно, я переписал IF заявление (и содержание) как:

IF(m.GT.1)THEN 
    !build format statement for write 
    WRITE(mm1,*)m-1 
    mm1=ADJUSTL(mm1) 
    !implied do write statement 
    WRITE(10,'('//TRIM(mm1)//'(i9,1x,f7.5/),i9,1x,f7.5)')(x(j),y(j),j=1,m) 
ELSEIF(m.EQ.1)THEN 
    WRITE(10,'(i9,1x,f7.5)')x(1),y(1) 
ENDIF 

Это создает формат оператора в соответствии с # значений должны быть выписаны, а затем делает один оператор записи для вывода вещей. Я обнаружил, что этот код на самом деле работает медленнее с этой формулировкой. Для справки я видел значительное ускорение в одной и той же системе (аппаратное и программное обеспечение) при переходе к подразумеваемой инструкции do write, когда количество данных, которые должны быть записаны, было исправлено. В предположении, что сам оператор WRITE работает быстрее, тогда это означало бы накладные расходы из пары строк, строящих это утверждение, что занимает дополнительное время, но это кажется трудным поверить. Для справки, m может варьироваться в зависимости от суммы, но, вероятно, в среднем составляет не менее 1000. Является ли конкатенация строк // очень медленным оператором, или есть что-то еще, что мне не хватает? Заранее спасибо.

+1

Что делать, если вы просто используете 'write (10, '(i9, f8.5)') (...)' и полагаетесь на реверсию формата? – francescalus

+0

Ore использует '*' как множитель или просто любое большое число, например 999999. –

+0

Фактический формат вывода имеет еще несколько значений, так что '*' может привести к нежелательной обертке строк. Но я понятия не имел, что подход francescalus не указывать, а VladimirF просто использовать число, которое всегда больше, чем 'm', были допустимыми параметрами. Я попробую их и обновить. – TTT

ответ

2

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

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

ненужная сложность возникает из двух областей:

  • у вас есть отдельные случаи для одного/более одной пары;
  • для более чем одного случая вы создаете формат, включающий в себя «динамический» счетчик повторов.

Как Vladimir F comments вы могли бы просто использовать очень большое число повторений: это не является ошибочным для редактирования, дескриптор должен быть обработан, когда нет больше элементов, которые должны быть записаны. Выход завершается (успешно) при достижении такого несоответствующего дескриптора. Тогда вы могли бы просто написать

WRITE(10,'(*(i9,1x,f7.5/))') (x(j),y(j),j=1,m) ! * replacing a large count 

скорее, чем конструкция if и создание формата.

Теперь это не совсем соответствует вашему первому результату. Как я упоминал выше, завершение вывода происходит, когда дескриптор редактирования данных достигается, когда нет соответствующего элемента для вывода. Это означает, что / будет обработан до того, как это произойдет: у вас есть окончательная пустая запись.

двоеточие Дескриптор полезен здесь:

WRITE(10,'(*(i9,1x,f7.5,:,/))') (x(j),y(j),j=1,m) 

При достижении : обработки немедленно прекращается, если не осталось выходной элемент для обработки.

Но мой предпочтительный подход является гораздо проще

WRITE(10,'(i9,1x,f7.5)') (x(j),y(j),j=1,m) ! No repeat count 

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

Являются ли эти вещи более быстрыми, но они, безусловно, делают код более чистым и понятным.

Как последнее примечание, это было модно избегать дополнительного редактирования X. Если ваши цифры помещаются внутри поля ширины 7, то 1x,f7.5 может быть заменен на f8.5 и иметь такой же вид: представление правомерно в поле. Утверждалось, что это снижение имеет преимущества в производительности при меньшем количестве дескрипторов.

+0

Я думаю, у вас есть опечатка. Когда я делаю 'WRITE (10, '(i9,1x, f7.5 /)')', он добавляет дополнительную пустую строку между каждой строкой вывода, включая последнюю. Но без косой черты 'WRITE (10, '(i9,1x, f7.5)')', он работает просто отлично. – TTT

+0

Действительно. Я исправлю, спасибо. – francescalus

+0

Подробности об ускорении: файл составляет 114 ГБ, я работаю на довольно эффективном NAS, ориентированном на большие файлы. Ваш рекомендуемый метод 'WRITE (10, '(i9,1x, f7.5)') (x (j), y (j), j = 1, m) 'сбрил 52 минуты от общего времени выполнения. Я не пытался определить, сколько времени было посвящено только выходу файла, но на основе того, что я знаю о производительности кода, это эквивалентно, по крайней мере, 100% ускорению для этой части кода, возможно, до до ~ 500%. – TTT

Смежные вопросы