О пересылки данных или когда выгодно считать на CUDA.
Открываем документ «CUDA C Best Practices Guide» (у меня версия от 8/20/2010) и читаем раздел «What Runs on a CUDA-Enabled Device?»
Цитата
To use CUDA, data values must be transferred from the host to the device along the PCI Express (PCIe) bus. These transfers are costly in terms of performance and should be minimized. This cost has several ramifications:
The complexity of operations should justify the cost of moving data to and from the device. Code that transfers data for brief use by a small number of threads will see little or no performance benefit. The ideal scenario is one in which many threads perform a substantial amount of work.
For example, transferring two matrices to the device to perform a matrix addition and then transferring the results back to the host will not realize much performance benefit. The issue here is the number of operations performed per data element transferred. For the preceding procedure, assuming matrices of size NЧN, there are N**2 operations (additions) and 3N**2 elements transferred, so the ratio of operations to elements transferred is 1:3 or O(1). Performance benefits can be more readily achieved when this ratio is higher. For example, a matrix multiplication of the same matrices requires N**3 operations (multiply-add), so the ratio of operations to elements transferred is O(N), in which case the larger the matrix the greater the performance benefit. The types of operations are an additional factor, as additions have different complexity profiles than, for example, trigonometric functions. It is important to include the overhead of transferring data to and from the device in determining whether operations should be performed on the host or on the device.
Другими словами, вычисления должны оправдывать пересылку данных. Сложность вычисления над одним элементом матрицы должна быть достаточно высокой.
А когда это происходит?
Рассмотрим вариант с
синхронной передачей данных (с асинхронной передачей и гетерогенные вычисления рассмотрим позже). Это такой вид передачи, когда мы закинули данные на карту и ничего не делаем пока не получим результат. Допустим, у нас есть две матрицы и мы хотим сделать что-то очень сложное с ними, причем на столько сложное, что пока мы считаем текущую КЭШ линию, последующая успевает подойти к нам из RAM благодаря:
- hardware prefetcher’ам (при выполнении на CPU) или умело расставленным prefetch инструкциям (при выполнении на карте)
- и умелому использованию таких техник оптимизации кода как loop-blocking
т.е. для нас не являются проблемами:
- шина памяти
- пинг-понг КЭШ линеек из-за false-sharing
- излишняя синхронизация
- дисбаланс нагрузки между ядрами
- вытеснение нужных данных из shared КЭШа (для CPU)
- невыравненность данных и прочая лабуда
В результате чего мы имеем практически идеальную масштабируемость которая позволяет работать практически на пике производительности (как бы в это не хотели верить некоторые здесь «спецы»)
Для того, чтобы вычисление такого кода на карте оправдывалось нам нужно соблюдение условия:
Код
T(cpu) > T(transfer) + T(cuda)
T(cpu) время выполнения вычислений на CPU
T(transfer) время пересылки данных
T(cuda) время выполнения вычислений на карте
В первом моем посте я показал, что вычисления на CUDA C2050 могут быть быстрее вычислений на Nehalem максимум в 10 раз.
Подставим это в формулу:
Код
T(cpu) > T(transfer) + 1/10 T(cpu)
9/10 T(cpu) > T(transfer)
Допустим, время пересылки включает в себя пересылку двух матриц (1.5Gb+1.5Gb) на карту и скачивание результата в виде одной матрицы (1.5Gb) обратно. Для этого нам нужно прокачать 4.5Gb через PCI x16 со скоростью 8Gb/сек. На это нам потребуется 0.56 сек. Т.е.
Код
9/10 T(cpu) > 0.56 сек, T(cpu) > 0.63 сек
Если сложность вычислений для приведенных выше двух матриц высокая и время на их обработку больше 0.63 сек, то вычисления на карте оправдают перекачку данных. Если же нужно обработать несколько пар таких матриц и обработка этих пар занимает несколько часов на CPU, то нужно брать в расчет время вычисления одной пары при условии что используется только
синхронная передача данных.