Mathematica-Числени изчисления
- Набор от знаци
- Структури на данни
- Числени изчисления
- Шаблони
- Съвет
- CUDA
Тук ще разгледаме основните методи за ускоряване и намаляване на паметта, използвана от вашите програми.
Подготовка Редактиране
Първо, когато работите с големи количества данни, не трябва да съхранявате историята на изчисленията.
Нека добавим удобна функция за отчитане на времето
Пакетирани масиви Редактиране
Важна роля играят така наречените пакетирани масиви. Ако всички елементи на масива са числа (цели, дробни или комплексни), тогава те могат да се съхраняват не като списък, а просто като обикновен масив (както в C, Fortran и т.н.). Това значително намалява консумацията на памет и увеличава скоростта.
Можете да използвате функции като Table, Array и ConstantArray, за да създавате кутийни масиви.
- Таблицата е най-гъвкавата функция, но, без да знаете, можете лесно да получите неопакован масив в нея.
- Array е по-аскетична функция, която използва чисти функции. Това няма да ви даде място за грешка и най-вероятно ще създаде опакован масив.
- ConstantArray - създава масив от еднакви елементи, нищо повече.
Обмислете различни опции за създаване на двуизмерен масив. Нека изчислим колко байта се изразходват за всеки елемент от масива и в същото време да определим времето за работа.
- Вижда се, че t1 се оказва изрично разопакован масив - той изразходва 32 байта за цяло число, вместо 4 набора. И времето за изпълнение е невероятно дълго. Факт е, че функцията Table има атрибут HoldAll, така че не знае какво е константа и в резултат на това не знае размера на получения масив и не може да го опакова.
- Когато създавахме t2, ние изрично посочихме константа с помощта на командата With и получихме пакетиран масив.
- Масивът a от самото начало се оказа опакован.
- Масивът ca със същите елементи също е опакован, но създаването му отнема много по-малко време.
Редактиране на автокомпилация
За големи изчисления във функции като Sum, Math използва автоматично компилиране, когато е възможно. Правилното му използване ви позволява да ускорите работата с 2 порядъка. Нека да разгледаме как работи автокомпилацията на примера за изчисляване на $ \sum_^N\frac $.
Първо, нека опитаме в челото
Тук функцията AbsoluteTiming връща списък, където първият елемент е изминалото време в секунди, а вторият е резултатът от самата операция.
Математиката, без да подозира нищо, изчисли рационална дроб за нас, като отдели много време за това. Числената стойност на предишната операция може да бъде намерена с помощта на
Сега нека започнем да оптимизираме нашите изчисления. Да започнем с външния вид. Много по-удобно е да използвате еквивалента във формата
Наивното желание за числено изчисление се крие в
Получихме само лек тласък. Работата е там, че функцията Sum, подобно на функцията Table, има атрибут HoldAll и Mathematics не знае какво е константа и не може да приложи автокомпилация. Както при Table, дефинирайте локална константа с командата With
Чудесен! Сега автокомпилирането работи и получихме увеличение на скоростта от 20 пъти. Нека продължим да оптимизираме. Сега математиката изчислява съотношението на цели числа и едва след това изчислява числената стойност. Нека направим числителя реален, тогава всичко останало автоматично ще бъде също реално
Получих още един малък, но хубавускорение. Нека сега си припомним многоядрения характер на съвременните процесори. Командата Parallelize превръща функцията Sum в ParallelSum. Стартирайте паралелни нишки ръчно с помощта на LaunchKernels, за да изключите времето на тяхното стартиране от изчислението на времето
Благодарение на паралелизацията получихме ускорение още 3 пъти (на 4-ядрен процесор).
В резултат на това, благодарение на компетентното използване на автокомпилация, получихме ускорение от 2 порядъка. Нека да видим какви други подобни функции имат автокомпилация
Стойностите, които виждате, са праговете на задействане за съответните автокомпилации. За малки изчисления автокомпилирането е безполезно. повече време за компилиране.
P.S. Съществуващата функция NSum е от малко значение, тъй като изчислява първите няколко члена, а опашката екстраполира и изчислява приблизително. В този пример тя работи много дълго време и псува много.
P.P.S. Ако вземем > 250 , тогава автокомпилирането работи без командата With. В многомерни функции като сума, произведение и др. границите на индекса могат да зависят от други индекси, така че е по-добре изрично да посочите, че границите са константи с With.
Редактиране на компилация
Нека продължим да оптимизираме примера от предишния параграф и сега използваме ръчна компилация
Този пример не използва паралелизиране и е малко по-бърз от съответния пример за автокомпилиране. Това е естествено, т.к При автоматично компилиране се изразходва време и за самата компилация. Обърнете внимание, че вътре в командата Compile съотношението цяло число връща реално число. За изчисляване на цялата част има команда Коефициент .
По-ефективно е да се използва компилация в C, която се появи в последните версии на Mathematics. Да проверим коекомпилатори в системата виж Матем
По подразбиране най-вероятно ще бъде GCC
Настройките на компилатора са описани по-подробно в документацията. Компилирайте до C
Вече има добро ускорение от 4 пъти. Нека добавим още една опция "Скорост". Без него математиката извършва серия от доста безполезни операции, които губят време. Например, той следи за препълване на целочислени променливи и, ако това се случи, продължава изчислението без компилиране
Сега ускорението е минимално, но при реални задачи може да бъде няколко пъти. Нека да видим какъв вид C файл създава Math
#include "math.h" #include "WolframRTL.h"
статични WolframCompileLibrary_Functions funStructCompile; статичен монетен двор I0_0 ; статичен монетен двор I0_2 ; статичен mreal R0_3; статичен mbool инициализиране = 1;
DLLEXPORT int Initialize_compute ( WolframLibraryData libData ) < ако (инициализиране) < funStructCompile = libData -> compileLibraryFunctions; I0_2 = (ментов) 0; I0_0 = (монета) 200; R0_3 = (mreal) 0. ; инициализиране = 0; > върни 0 ; >
DLLEXPORT void Uninitialize_compute ( WolframLibraryData libData ) < ако (! инициализиране) < инициализиране = 1; > >
DLLEXPORT int compute ( WolframLibraryData libData , mreal * Res ) < ментов I0_1 ; ментов I0_3 ; ментов I0_4 ; ментов I0_5 ; ментов I0_6 ; ментов I0_7 ; ментов I0_8 ; mreal R0_0; mreal R0_1; mreal R0_2; mreal R0_4; mreal R0_5; R0_0 = R0_3; I0_1 = I0_0; I0_3 = I0_2; отидете на lab18 ;
lab5 : I0_4 = I0_0 ; I0_5 = I0_2; отидете на lab17 ;
lab8 : I0_6 = I0_0 ; I0_7 = I0_2; отидете на lab16 ;
lab11 : I0_8 = I0_3 + I0_5 + I0_7 ; R0_4 = (mreal)I0_8 ; R0_5 = 1 / R0_4; R0_4 = R0_0 + R0_5; R0_0 = R0_4;
lab16 : ако ( ++ I0_7 I0_6 ) < отидете на lab11 ; >
lab17 : ако ( ++ I0_5 I0_4 ) < отидете на лаборатория8; >
lab18 : ако ( ++ I0_3 I0_1 ) < отидете на lab5 ; >
* Res = R0_0; funStructCompile -> WolframLibraryData_cleanUp (libData, 0); върни 0 ; >
Кодът е отвратителен, но компилаторът го усвоява успешно.
Добавете още флагове за оптимизация (имаме GCC)
Ускорение от 40% също не е лошо. И сега най-интересното е успоредяването. Нека паралелизираме външната сума и въведем два аргумента в нашата програма - началния и крайния индекс на външната сума. Различните теми ще изчислят различни части от сумата
Началото и краят на броенето се предават като списъци. Програмата изчислява успоредно сумата от 1 до 50, от 51 до 100, от 101 до 150 и от 151 до 200. Накрая сумираме резултата с функцията Total и получаваме още 3 пъти ускорение.
В резултат на това ръчната компилация направи възможно ускоряването на изчисленията с друг порядък и в сравнение с наивния първи пример в предишния параграф, с 3 порядъка.
Intel C CompilerEdit
Тези, които искат да експериментират, могат да свържат Intel C Compiler вместо GCC. Той е безплатен за некомерсиална употреба на Linux. В Gentoo най-новата версия е налична в наслагването на науката и се инсталира по стандартния начин.
Уверете се, че Math вижда ICC
Променете компилатора по подразбиране на ICC
За да дефинирате променливи на средата, Math извиква (в 64-битовата версия) /opt/intel/composerxe-2011.9.293/bin/intel64/iccvars_intel64.sh. Но такъв файл може да не съществува поради разлика в ICC версията. Във версия 12 вместо това се използва командата
Да видим какво се случи:
Опциите „ShellCommandFunction“ и „ShellOutputFunction“ са написани тук, за да видите какво се случва. Параметрите за оптимизация са зададени за ICC. Ако всичко е компилирано без грешки, можете да проверите времето за изпълнение
В този случай практически няма ускорение от използването на Intel C Compiler, но може да бъде забележимо в реални примери.