Merge pull request #2549 from SunSerega/POCGL-stables
POCGL: Обновление OpenCLABC и расширений OpenCL
This commit is contained in:
commit
ba485bfd33
|
|
@ -15,8 +15,8 @@ begin
|
|||
prog['TEST'].Exec1(10, // Используем 10 ядер
|
||||
|
||||
// Заполняем весь массив значениями (1), прямо перед выполнением
|
||||
//ToDo Разобраться почему надо KernelArg.From
|
||||
KernelArg.FromCLArrayCQ(A.NewQueue.AddFill(1))
|
||||
//TODO Разобраться почему надо KernelArg.From
|
||||
KernelArg.FromCLArrayCQ(A.NewQueue.AddFillValue(1))
|
||||
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ try
|
|||
|
||||
end, fps);
|
||||
|
||||
//ToDo Использовать CLArray2<byte> когда будет
|
||||
//TODO Использовать CLArray2<byte> когда будет
|
||||
var B := new MemorySegment(W*W*sizeof(byte));
|
||||
B.WriteArray2&<byte>(MatrGen(W,W, (x,y)->byte(Random(2))));
|
||||
var B_temp := new MemorySegment(B.Size);
|
||||
|
|
|
|||
|
|
@ -15,8 +15,8 @@ begin
|
|||
|
||||
prog['TEST'].Exec1(10, // Используем 10 ядер
|
||||
|
||||
//ToDo Разобраться почему надо KernelArg.From
|
||||
KernelArg.FromCLArrayCQ(A.NewQueue.AddFill(1))
|
||||
//TODO Разобраться почему надо KernelArg.From
|
||||
KernelArg.FromCLArrayCQ(A.NewQueue.AddFillValue(1))
|
||||
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -1142,8 +1142,10 @@ var k := code['KernelName'];
|
|||
...
|
||||
var ks := code.GetAllKernels;
|
||||
ks.PrintLines;
|
||||
</code></pre>
|
||||
<hr />
|
||||
</code></pre>
|
||||
</div>
|
||||
<script>on_start_folder("Kernel", document.getElementById("page-11"))</script>
|
||||
<div id="page-12" page_name="KernelArg" hidden=true>
|
||||
<p>Методы, запускающие <code>Kernel</code> принимают специальные аргументы типа <code>KernelArg</code>, которые передаются в OpenCL-C код.<br />
|
||||
Экземпляр <code>KernelArg</code> может быть создан из нескольких типов значений, а точнее:</p>
|
||||
<pre><code>## uses OpenCLABC;
|
||||
|
|
@ -1170,7 +1172,7 @@ k.Exec1(1,
|
|||
// И указатель на размерное значение
|
||||
// (в kernel попадёт само значение, не указатель)
|
||||
@val2,
|
||||
// Так нельзя, потому что val1 уже захвачена лямбдой из HFQ
|
||||
// Так нельзя, потому что val1 была захвачена лямбдой из HFQ
|
||||
// @val1,
|
||||
// Расширенный набор параметров для передачи адреса
|
||||
KernelArg.FromPtr(new System.IntPtr(@val2), new System.UIntPtr(sizeof(integer)))
|
||||
|
|
@ -1182,9 +1184,9 @@ k.Exec1(1,
|
|||
только как тонкую оптимизацию, потому что много чего может пойти не так.
|
||||
Если передавать <code>@val2</code> в качестве <code>KernelArg</code> - надо знать все тонкости.</p>
|
||||
<hr />
|
||||
<p>Если <code>@val2</code> передали вкачестве <code>KernelArg</code>:</p>
|
||||
<p>Если <code>@x</code> передали вкачестве <code>KernelArg</code>:</p>
|
||||
<ol>
|
||||
<li><p><code>val2</code> не может быть глобальной переменной или полем класса:</p>
|
||||
<li><p><code>x</code> не может быть глобальной переменной или полем класса:</p>
|
||||
<pre><code>uses OpenCLABC;
|
||||
|
||||
type
|
||||
|
|
@ -1219,14 +1221,13 @@ end;
|
|||
begin end.
|
||||
</code></pre>
|
||||
</li>
|
||||
<li><p><code>val2</code> не должно быть захвачего лямбдой.<br />
|
||||
Хотя указатель на <code>val2</code> уже можно захватывать:</p>
|
||||
<li><p><code>x</code> не должно быть захвачего лямбдой.<br />
|
||||
Хотя указатель на <code>x</code> уже можно захватывать:</p>
|
||||
<pre><code>uses OpenCLABC;
|
||||
|
||||
begin
|
||||
var k: Kernel;
|
||||
var val1 := 3;
|
||||
var val2 := 5;
|
||||
|
||||
// На val1 всё ещё накладываются все ограничени,
|
||||
// когда val1_ptr использована в качестве KernelArg
|
||||
|
|
@ -1236,7 +1237,7 @@ begin
|
|||
k.Exec1(1,
|
||||
|
||||
val1,
|
||||
HFQ(()->val1_ptr^), // захватили val1_ptr, а не val1
|
||||
HFQ(()->val1_ptr^), // захватили переменную val1_ptr, а не val1
|
||||
|
||||
// val1 нигде не захвачена, поэтому теперь так можно
|
||||
@val1,
|
||||
|
|
@ -1247,19 +1248,13 @@ begin
|
|||
end.
|
||||
</code></pre>
|
||||
</li>
|
||||
<li><p>Выходить из подпрограммы, где объявили <code>val2</code> нельзя, пока <code>.Exec</code> не закончит выполнятся.
|
||||
Это так же значит, что возвращать очередь, содержащую <code>KernelArg</code> из <code>@val2</code> обычно нельзя.<br />
|
||||
Но это можно обойти, если объявлять переменную в другой подпрограмме, а дальше передавать только её адрес:</p>
|
||||
<li><p>Выходить из подпрограммы, где объявили <code>x</code> нельзя, пока <code>.Exec</code> не закончит выполнятся.
|
||||
Это так же значит, что возвращать очередь, содержащую <code>KernelArg</code> из <code>@x</code> обычно нельзя.<br />
|
||||
Но это можно обойти, если объявлять переменную в другой подпрограмме:</p>
|
||||
<pre><code>uses OpenCLABC;
|
||||
|
||||
var k: Kernel; // Вообще лучше передавать параметром в p2
|
||||
|
||||
// Обратите внимание - val принимает var-параметром
|
||||
// То есть, p2 принимает адрес val
|
||||
// (можно так же принимать указатель, но это не так удобно)
|
||||
// Иначе переменная будет копироваться при передаче
|
||||
// То есть без var перед параметром - val тут
|
||||
// будет новой переменной, объявленной в p2 а не в p1
|
||||
function p2(var val: integer): CommandQueueBase;
|
||||
begin
|
||||
|
||||
|
|
@ -1286,18 +1281,24 @@ begin
|
|||
p1;
|
||||
end.
|
||||
</code></pre>
|
||||
<p>Тут <code>val</code> объявлена в <code>p1</code>. При этом <code>val</code> в <code>p2</code> является синонимом <code>val</code> из <code>p1</code>,
|
||||
потому что объявлена <code>var</code>-параметром - передачей по ссылке.<br />
|
||||
Если передать без <code>var</code> перед параметром - <code>val</code> из <code>p2</code> будет копией,
|
||||
а значит перестанет существовать после выхода из <code>p2</code>.</p>
|
||||
</li>
|
||||
</ol>
|
||||
<p>Компилятор не заставит вас следовать этим ограничениям. И программа может даже работать, игнорируя большинство сказанного тут.<br />
|
||||
Но потом вы добавите что то в совсем другой части программы, или запустите её на
|
||||
другом компьютере и она вдруг начнёт выводить мусорные значения, или падать с ошибками
|
||||
вроде <code>AccessViolationException</code>, которые совершенно не объясняют в чём проблема.</p>
|
||||
<p>Если очень интересно откуда берутся эти ограничения - начните с применения .Net декомпилятора к .exe файлам,
|
||||
получаемым при компиляции программ с данной страницы. Что останется непонятно - можете спрашивать в issue.</p>
|
||||
<p>Обратите внимание: Компилятор не заставит вас следовать описанным тут ограничениям.
|
||||
И показанные на этой странице коды могут работать не смотря на эти ограничения,
|
||||
потому что саму область памяти, на которую ссылается <code>KernelArg</code> в нём, никто никогда не уничтожает.</p>
|
||||
<p>Но в то же время эту же область памяти может использовать под другие данные, таким образом
|
||||
заменяя значение которое вы пытались передать в GPU на мусорные данные.</p>
|
||||
<p>Это плохо в первую очередь потому, что на перезапись этих данных может повлиять совершенно не связанная
|
||||
часть кода, или различие архитектур компьтеров, таким образом усложняя поиск источника ошибки.</p>
|
||||
<p>Поэтому, ещё раз, используйте передачу адреса в качестве <code>KernelArg</code> только как тонкую оптимизацию и только когда понимаете что делаете.</p>
|
||||
</div>
|
||||
<script>on_start_folder("Kernel", document.getElementById("page-11"))</script>
|
||||
<script>on_page_added(document.getElementById("page-12"))</script>
|
||||
<script>on_end_folder()</script>
|
||||
<div id="page-12" page_name="" hidden=true>
|
||||
<div id="page-13" page_name="" hidden=true>
|
||||
<p>Обычные программы невозможно запустить на GPU. Для этого надо писать особые программы.<br />
|
||||
В контексте OpenCL - эти программы обычно пишутся на языке "OpenCL C" (основанном на языке "C").</p>
|
||||
<p>Язык OpenCL-C это часть библиотеки OpenCL, поэтому его справку можно найти <a href="https://www.khronos.org/registry/OpenCL/">там же</a>, где и справку OpenCL.</p>
|
||||
|
|
@ -1313,8 +1314,8 @@ end.
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<script>on_start_folder("ProgramCode", document.getElementById("page-12"))</script>
|
||||
<div id="page-13" page_name="Создание из исходного кода" hidden=true>
|
||||
<script>on_start_folder("ProgramCode", document.getElementById("page-13"))</script>
|
||||
<div id="page-14" page_name="Создание из исходного кода" hidden=true>
|
||||
<p>Конструктор <code>ProgramCode</code> (<code>new ProgramCode(...)</code>) принимает тексты исходников программы на языке OpenCL-C.<br />
|
||||
<strong>Именно тексты исходников, не имена файлов!</strong></p>
|
||||
<p>Так же, как исходники паскаля хранят в .pas файлах, исходники OpenCL-C кода хранят в .cl файлах.
|
||||
|
|
@ -1322,8 +1323,8 @@ end.
|
|||
<p>Так как конструктор <code>ProgramCode</code> принимает текст - исходники программы на OpenCL-C можно хранить даже в
|
||||
строке в .pas программе. Тем не менее, храненить исходники OpenCL-C кода в .cl файлах обычно удобнее всего.</p>
|
||||
</div>
|
||||
<script>on_page_added(document.getElementById("page-13"))</script>
|
||||
<div id="page-14" page_name="Создание из бинарного файла" hidden=true>
|
||||
<script>on_page_added(document.getElementById("page-14"))</script>
|
||||
<div id="page-15" page_name="Создание из бинарного файла" hidden=true>
|
||||
<p>После создания объекта типа <code>ProgramCode</code> из исходников можно вызвать
|
||||
метод <code>ProgramCode.SerializeTo</code>, чтобы сохранить код в бинарном и прекомпилированном виде.
|
||||
Обычно это делается отдельной программой (не той же самой, которая будет использовать этот бинарный код).</p>
|
||||
|
|
@ -1331,10 +1332,10 @@ end.
|
|||
используя статический метод <code>ProgramCode.DeserializeFrom</code>.</p>
|
||||
<p>Пример можно найти в папке <code>Прекомпиляция ProgramCode</code> или <a href="https://github.com/SunSerega/POCGL/tree/master/Samples/OpenCLABC/%D0%9F%D1%80%D0%B5%D0%BA%D0%BE%D0%BC%D0%BF%D0%B8%D0%BB%D1%8F%D1%86%D0%B8%D1%8F%20ProgramCode">тут</a>.</p>
|
||||
</div>
|
||||
<script>on_page_added(document.getElementById("page-14"))</script>
|
||||
<script>on_page_added(document.getElementById("page-15"))</script>
|
||||
<script>on_end_folder()</script>
|
||||
<script>on_end_folder()</script>
|
||||
<div id="page-15" page_name="" hidden=true>
|
||||
<div id="page-16" page_name="" hidden=true>
|
||||
<p>Передавать команды для GPU по одной не эффективно.
|
||||
Гораздо эффективнее передавать несколько команд сразу.</p>
|
||||
<p>Для этого существуют очереди (типы, наследующие от <code>CommandQueue<T></code> и <code>CommandQueueBase</code>).
|
||||
|
|
@ -1353,8 +1354,8 @@ end.
|
|||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<script>on_start_folder("Очередь [команд] (CommandQueue)", document.getElementById("page-15"))</script>
|
||||
<div id="page-16" page_name="" hidden=true>
|
||||
<script>on_start_folder("Очередь [команд] (CommandQueue)", document.getElementById("page-16"))</script>
|
||||
<div id="page-17" page_name="" hidden=true>
|
||||
<p>У каждого типа-очереди есть свой тип возвращаемого значения.<br />
|
||||
К примеру, так объявляется переменная в которую можно будет сохранить очередь, возвращающую <code>integer</code>:</p>
|
||||
<pre><code>var Q1: CommandQueue<integer>;
|
||||
|
|
@ -1399,9 +1400,9 @@ OtpObject( Context.Default.SyncInvoke( HPQ( ()->Print('Выполнилас
|
|||
или применять операции преобразования очередей, как <code>.ThenConvert</code>,
|
||||
то тип возвращаемого значения будет восприниматься как <code>Object</code>.</p>
|
||||
</div>
|
||||
<script>on_start_folder("Возвращаемое значение очередей", document.getElementById("page-16"))</script>
|
||||
<script>on_start_folder("Возвращаемое значение очередей", document.getElementById("page-17"))</script>
|
||||
<script>on_end_folder()</script>
|
||||
<div id="page-17" page_name="" hidden=true>
|
||||
<div id="page-18" page_name="" hidden=true>
|
||||
<p>Самый простой способ выполнить очередь - вызвать метод <code>Context.SyncInvoke</code>.<br />
|
||||
Он синхронно выполняет очередь и вызвращает её результат.</p>
|
||||
<p>Но если надо выполнить очередь асинхронно - лучше использовать метод <code>Context.BeginInvoke</code>.<br />
|
||||
|
|
@ -1436,9 +1437,9 @@ end;
|
|||
</code></pre>
|
||||
<p>Для этого кода есть стандартный снипет. Чтобы активировать его - напишите <code>tryo</code> и нажмите Shift+Пробел.</p>
|
||||
</div>
|
||||
<script>on_start_folder("Выполнение очередей", document.getElementById("page-17"))</script>
|
||||
<script>on_start_folder("Выполнение очередей", document.getElementById("page-18"))</script>
|
||||
<script>on_end_folder()</script>
|
||||
<div id="page-18" page_name="" hidden=true>
|
||||
<div id="page-19" page_name="" hidden=true>
|
||||
<h1>Есть всего 10 базовых способов создать очередь:</h1>
|
||||
<ol>
|
||||
<li><p><a path="Из объекта с коммандами"> Из объекта с коммандами </a></p>
|
||||
|
|
@ -1463,8 +1464,8 @@ end;
|
|||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
<script>on_start_folder("Создание очередей", document.getElementById("page-18"))</script>
|
||||
<div id="page-19" page_name="Из объекта с коммандами" hidden=true>
|
||||
<script>on_start_folder("Создание очередей", document.getElementById("page-19"))</script>
|
||||
<div id="page-20" page_name="Из объекта с коммандами" hidden=true>
|
||||
<p>Самый просто способ создать очередь — выбрать объект, имеющий методы, соответствующие командам для GPU, и вызвав его метод <code>.NewQueue</code>.</p>
|
||||
<p>Полученная очередь будет иметь особый тип, с припиской CCQ (что значит "Command Container Queue", то есть очередь-контейнер для коман GPU).<br />
|
||||
К примеру, метод <code>CLArray<byte>.NewQueue</code> вернёт очередь типа <code>CLArrayCCQ<byte></code>.<br />
|
||||
|
|
@ -1497,10 +1498,30 @@ Context.Default.SyncInvoke(q.AddGetArray).Println;
|
|||
<pre><code>var q0: CommandQueue<MemorySegment>;
|
||||
...
|
||||
var q := new MemorySegmentCCQ(q0);
|
||||
</code></pre>
|
||||
</code></pre>
|
||||
<hr />
|
||||
<p>Команды объектов, представляющих память на GPU можно разделить на группы.</p>
|
||||
<p>По направлению передачи:</p>
|
||||
<ol>
|
||||
<li><code>Write</code> и <code>Fill</code>: Из RAM в память GPU;</li>
|
||||
<li><code>Read</code>: Из памяти GPU в RAM;</li>
|
||||
<li><code>Copy</code>: Между двумя областями памяти GPU.</li>
|
||||
<li><code>Get</code>: Как <code>Read</code>, но вместо использования существующей памяти RAM, выделяется новая.</li>
|
||||
</ol>
|
||||
<p>И по типу данных на стороне RAM:</p>
|
||||
<ol>
|
||||
<li><code>Data</code>: Используются данные, находящиеся по указанному адресу;</li>
|
||||
<li><code>Value</code>: Используется копия указанного размерного значения;</li>
|
||||
<li><code>Array</code>: Используется содержимое указанного массива размерных значений.</li>
|
||||
</ol>
|
||||
<p>Но при этом отсутствуют некоторые комбинации.</p>
|
||||
<p>В первую очередь, в случае <code>Copy</code> нет понятия типа данных на стороне RAM, потому что RAM в принципе не задействуется.</p>
|
||||
<p>Так же нет <code>ReadValue</code>, потому что нет смысла читать в копию указанного значения.<br />
|
||||
Вместо него надо использовать <code>GetValue</code> - создающее и возвращающее новое размерное значение.<br />
|
||||
Или <code>ReadData</code>, передавая адрес значения, которое надо перезаписать - но с этим надо осторожно, как и в случае <a path="../../Простые обёртки/Kernel/KernelArg"> <code>KernelArg</code> </a>.</p>
|
||||
</div>
|
||||
<script>on_page_added(document.getElementById("page-19"))</script>
|
||||
<div id="page-20" page_name="Из готового результата" hidden=true>
|
||||
<script>on_page_added(document.getElementById("page-20"))</script>
|
||||
<div id="page-21" page_name="Из готового результата" hidden=true>
|
||||
<p>Переменной очереди можно присвоить значение, тип которого совпадает с возвращаемым значением очереди:</p>
|
||||
<pre><code>var q: CommandQueue<integer> := 5;
|
||||
</code></pre>
|
||||
|
|
@ -1520,8 +1541,8 @@ var q := new MemorySegmentCCQ(q0);
|
|||
Writeln($'Очередь не константная');
|
||||
</code></pre>
|
||||
</div>
|
||||
<script>on_page_added(document.getElementById("page-20"))</script>
|
||||
<div id="page-21" page_name="С кодом для CPU" hidden=true>
|
||||
<script>on_page_added(document.getElementById("page-21"))</script>
|
||||
<div id="page-22" page_name="С кодом для CPU" hidden=true>
|
||||
<p>Иногда между командами для GPU надо вставить выполнение обычного кода на CPU.
|
||||
А разрывать для этого очередь на две части - плохо, потому что
|
||||
одна целая очередь всегда выполнится быстрее двух её частей.</p>
|
||||
|
|
@ -1584,8 +1605,8 @@ end.
|
|||
Даже потокобезопастный код, как вызов <code>Buffer.Dispose</code>, может привести к утечкам памяти, если посреди него кинут <code>ThreadAbortException</code>.</p>
|
||||
<p>Считайте получение <code>ThreadAbortException</code> критической ошибкой, после которой очень желателен перезапуск всего .exe файла.</p>
|
||||
</div>
|
||||
<script>on_page_added(document.getElementById("page-21"))</script>
|
||||
<div id="page-22" page_name="Комбинируя другие очереди" hidden=true>
|
||||
<script>on_page_added(document.getElementById("page-22"))</script>
|
||||
<div id="page-23" page_name="Комбинируя другие очереди" hidden=true>
|
||||
<p>Если сложить две очереди A и B (<code>var C := A+B</code>) — получится очередь C, в которой сначала выполнится A, а затем B.<br />
|
||||
Очередь C будет считаться выполненной тогда, когда выполнится очередь B.</p>
|
||||
<p>Если умножить две очереди A и B (<code>var C := A*B</code>) — получится очередь C, в которой одновременно начнут выполняться A и B.<br />
|
||||
|
|
@ -1653,8 +1674,8 @@ begin
|
|||
end.
|
||||
</code></pre>
|
||||
</div>
|
||||
<script>on_page_added(document.getElementById("page-22"))</script>
|
||||
<div id="page-23" page_name="Из очереди + преобразователя" hidden=true>
|
||||
<script>on_page_added(document.getElementById("page-23"))</script>
|
||||
<div id="page-24" page_name="Из очереди + преобразователя" hidden=true>
|
||||
<p>Если надо с минимальными затратами изменить представление компилятора об очереди - лучше всего использовать <code>.Cast</code>.<br />
|
||||
Но он ограничен примерно так же, как метод последовательностей <code>.Cast</code>. То есть:</p>
|
||||
<pre><code>uses OpenCLABC;
|
||||
|
|
@ -1702,12 +1723,12 @@ begin
|
|||
end.
|
||||
</code></pre>
|
||||
</div>
|
||||
<script>on_page_added(document.getElementById("page-23"))</script>
|
||||
<div id="page-24" page_name="Из повторения очереди" hidden=true>
|
||||
<script>on_page_added(document.getElementById("page-24"))</script>
|
||||
<div id="page-25" page_name="Из повторения очереди" hidden=true>
|
||||
<p><del>В данный момент всё ещё не работает... Но уже совсем скоро, правда-правда!</del></p>
|
||||
</div>
|
||||
<script>on_page_added(document.getElementById("page-24"))</script>
|
||||
<div id="page-25" page_name="Множественное использование очереди" hidden=true>
|
||||
<script>on_page_added(document.getElementById("page-25"))</script>
|
||||
<div id="page-26" page_name="Множественное использование очереди" hidden=true>
|
||||
<p>Одну и ту же очередь можно использовать несколько раз, в том числе одновременно:</p>
|
||||
<pre><code>uses OpenCLABC;
|
||||
|
||||
|
|
@ -1790,8 +1811,8 @@ end.
|
|||
<p>А если контекст разный - надо сохранять результат в переменную и использовать <code>Wait</code> очереди
|
||||
(<a path="Из ожидания очередей">подробнее на странице ниже</a>).</p>
|
||||
</div>
|
||||
<script>on_page_added(document.getElementById("page-25"))</script>
|
||||
<div id="page-26" page_name="Особые .Add методы" hidden=true>
|
||||
<script>on_page_added(document.getElementById("page-26"))</script>
|
||||
<div id="page-27" page_name="Особые .Add методы" hidden=true>
|
||||
<p>Между командами в <code>CCQ</code> очередях бывает надо вставить выполнение другой очереди или кода для CPU.</p>
|
||||
<p>Это можно сделать, используя несколько <code>.NewQueue</code>:</p>
|
||||
<pre><code>var s: MemorySegment;
|
||||
|
|
@ -1824,8 +1845,8 @@ q.AddQueue(q);
|
|||
<p>Все <code>.Add*</code> методы добавляют команды в существующую очередь, а не создают новую.
|
||||
Поэтому очередь, созданная предыдущим кодом при попытке выполнения начнёт циклически запускать саму себя.</p>
|
||||
</div>
|
||||
<script>on_page_added(document.getElementById("page-26"))</script>
|
||||
<div id="page-27" page_name="Из ожидания очередей" hidden=true>
|
||||
<script>on_page_added(document.getElementById("page-27"))</script>
|
||||
<div id="page-28" page_name="Из ожидания очередей" hidden=true>
|
||||
<p>Большинство деревьев выполнения очередей можно реализовать используя только сложение и умножение очередей. Но есть несколько проблем:</p>
|
||||
<ul>
|
||||
<li><p>Большинство но не все. Пример дерева которое нельзя реализовать через сложение и умножение можно найти <a href="https://github.com/SunSerega/POCGL/blob/master/Samples/OpenCLABC/Wait%20%D0%BE%D1%87%D0%B5%D1%80%D0%B5%D0%B4%D0%B8/1.pas">тут</a> или в файле:<br />
|
||||
|
|
@ -2023,8 +2044,8 @@ t1.Wait;
|
|||
t2.Wait;
|
||||
</code></pre>
|
||||
</div>
|
||||
<script>on_page_added(document.getElementById("page-27"))</script>
|
||||
<div id="page-28" page_name="Не создавая явно" hidden=true>
|
||||
<script>on_page_added(document.getElementById("page-28"))</script>
|
||||
<div id="page-29" page_name="Не создавая явно" hidden=true>
|
||||
<p>Передавать команды по одной, когда их несколько - ужасно не эффективно!<br />
|
||||
Но нередко бывает так, что команда всего одна. Или для отладки надо одноразово выполнить одну команду.</p>
|
||||
<p>Для таких случаев можно создавать очередь неявно:<br />
|
||||
|
|
@ -2055,9 +2076,9 @@ a[2] := 7;
|
|||
a.GetArray.Println;
|
||||
</code></pre>
|
||||
</div>
|
||||
<script>on_page_added(document.getElementById("page-28"))</script>
|
||||
<script>on_page_added(document.getElementById("page-29"))</script>
|
||||
<script>on_end_folder()</script>
|
||||
<div id="page-29" page_name="" hidden=true>
|
||||
<div id="page-30" page_name="" hidden=true>
|
||||
<p>Все методы создающие одну команду (<code>*CCQ.Add*</code> методы и все методы неявных очередей)
|
||||
могут принимать очередь вместо значения в качестве любого параметра. Но в таком случае
|
||||
возвращаемый тип очереди должен совпадать с типом параметра. К примеру:</p>
|
||||
|
|
@ -2085,10 +2106,10 @@ a.GetArray.Println;
|
|||
<p>Обычно вложенные очереди используются при вызове kernel'а,
|
||||
когда надо записать что-то в память GPU прямо перед вызовом kernel'а.</p>
|
||||
</div>
|
||||
<script>on_start_folder("Вложенные очереди", document.getElementById("page-29"))</script>
|
||||
<script>on_start_folder("Вложенные очереди", document.getElementById("page-30"))</script>
|
||||
<script>on_end_folder()</script>
|
||||
<script>on_end_folder()</script>
|
||||
<div id="page-30" page_name="" hidden=true>
|
||||
<div id="page-31" page_name="" hidden=true>
|
||||
<p>В данной справке в нескольких местах можно встретить утверждения вроде</p>
|
||||
<blockquote>
|
||||
<p>Вызовы <code>Context.BeginInvoke</code> стоит, по возможности, объединять.</p>
|
||||
|
|
@ -2103,8 +2124,8 @@ a.GetArray.Println;
|
|||
<li><a path="Внутренности BeginInvoke"> Что делает вызов <code>BeginInvoke</code> </a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<script>on_start_folder("Оптимизация", document.getElementById("page-30"))</script>
|
||||
<div id="page-31" page_name="Внутренности BeginInvoke" hidden=true>
|
||||
<script>on_start_folder("Оптимизация", document.getElementById("page-31"))</script>
|
||||
<div id="page-32" page_name="Внутренности BeginInvoke" hidden=true>
|
||||
<p>Кроме самого выполнения очередей - им так же необходима инициализация и финализация.</p>
|
||||
<hr />
|
||||
<h3>Инициализация</h3>
|
||||
|
|
@ -2144,7 +2165,7 @@ c1.BeginInvoke(A);
|
|||
<p>Внутренние оптимизации <code>OpenCLABC</code> делают этот код практически не отличимым по скорости, от <code>BeginInvoke(A+B)</code>.</p>
|
||||
<p>Единственное различие - время инициализации. Потому что <code>A</code> не запустится, пока не закончится обход дерева <code>c2.BeginInvoke</code>.</p>
|
||||
</div>
|
||||
<script>on_page_added(document.getElementById("page-31"))</script>
|
||||
<script>on_page_added(document.getElementById("page-32"))</script>
|
||||
<script>on_end_folder()</script>
|
||||
<script>on_end_folder()</script>
|
||||
</body>
|
||||
|
|
|
|||
2179
bin/Lib/OpenCL.pas
2179
bin/Lib/OpenCL.pas
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -326,7 +326,7 @@ type
|
|||
public function ToString: string; override := $'x_colormap[{val}]';
|
||||
end;
|
||||
|
||||
//ToDo что это вообще за типы? я не могу найти ничего про них
|
||||
//TODO что это вообще за типы? я не могу найти ничего про них
|
||||
x_vl_server = record
|
||||
public val: IntPtr;
|
||||
public constructor(val: IntPtr) := self.val := val;
|
||||
|
|
|
|||
Loading…
Reference in a new issue