This commit is contained in:
2026-02-17 23:13:20 +03:00
parent 65218abfb1
commit e52dde575a
429 changed files with 875 additions and 14 deletions

View File

View File

@ -0,0 +1,29 @@
CC = nvcc -arch=sm_75
DEBUG ?= false
DIRS = dist build
ifeq ($(DEBUG), false)
CC += -O3
else
CC += -g -G
endif
.PHONY: all run
all: $(DIRS) dist/app
dist/app: build/main.o build/op.o
$(CC) $^ -o $@ -lcuda
build/op.o: op.ptx
$(CC) $^ -dc -o $@
build/main.o: main.cu
$(CC) $^ -ptx -o build/main.ptx
$(CC) $^ -rdc=true -dc -o $@
$(DIRS):
mkdir -p $@
clean:
rm -rf $(DIRS)

View File

@ -0,0 +1,23 @@
[euclidean](https://web.archive.org/web/20230212044931/http://www-math.ucdenver.edu/~wcherowi/courses/m5410/exeucalg.html)
[ecdsa1](https://sefiks.com/2018/02/16/elegant-signatures-with-elliptic-curve-cryptography/)
[ecdsa2](https://learnmeabitcoin.com/technical/cryptography/elliptic-curve/ecdsa/)
[ptx](https://philipfabianek.com/posts/cuda-ptx-introduction)
высокий приоритет
6, 7 State Spaces / Properties of State Spaces Ключевое отличие от CPU! В CPU память в основном плоская (RAM, кэш). В GPU есть много типов памяти: глобальная (.global), общая для блока потоков (.shared), константная (.const), локальная (.local) и т.д. Это фундамент для написания производительного кода.
19 Cost Estimates for Accessing State-Spaces Прямое продолжение предыдущего пункта. Объясняет, какая память быстрая, а какая медленная. Критично для оптимизации.
4 Operator Precedence Синтаксис PTX похож на ассемблер, но с выражениями. Знать приоритет операторов необходимо.
8 Fundamental Type Specifiers Типы данных в PTX (.b8, .s16, .f32, .b64 и т.д.). Аналог byte, word, dword в x86, но с учетом специфики GPU.
3 Predefined Identifiers Предопределенные константы, такие как %tid, %ctaid, %ntid. Это основа модели выполнения CUDA! Вместо одного потока (RIP/EIP) у вас есть идентификаторы потока, блока и сетки.
20 Operation Types Классификация инструкций PTX. Поможет быстро ориентироваться в мануале.
1 PTX Directives Директивы ассемблера (.version, .target, .global). Аналог секций и директив в NASM (SECTION .text, global _start)
средний приоритет
21 Scopes Области видимости для атомарных операций и барьеров (.cta, .cluster, .gpu, .sys). Важно для синхронизации.
14, 40, 56 Различные таблицы про Swizzling и Layout Касаются продвинутых техник работы с памятью и матрицами для оптимизации доступа. Актуально для low-level оптимизаций, похоже на работу с выравниванием и SIMD в x86.
29 Summary of Floating-Point Instructions Обзор инструкций для чисел с плавающей точкой. На GPU они крайне важны.
30-32 Cache Operators / Eviction Priority Hints Управление кэшем. Продвинутая тема для тонкой настройки, аналогичная prefetch-инструкциям в x86.
53, 55, 56 Таблицы про MMA (Matrix Multiply-Accumulate) Инструкции для тензорных ядер (аналог FMA в x86, но для матриц). Сердце производительности в AI/HPC.
22-25 Comparison Operators Особенности сравнений для целых и вещественных чисел (учет NaN).

161
5/data science/1e/main.cu Normal file
View File

@ -0,0 +1,161 @@
#include <stdint.h>
template <typename T, int TILE_SIZE>
__global__ void mat_mul(T *A, T *B, T *C, int N, int M, int K) {
__shared__ T sA[TILE_SIZE][TILE_SIZE];
__shared__ T sB[TILE_SIZE][TILE_SIZE];
int bx = blockIdx.x, by = blockIdx.y;
int tx = threadIdx.x, ty = threadIdx.y;
int row = by * TILE_SIZE + ty;
int col = bx * TILE_SIZE + tx;
if (col >= K || row >= M) return;
T sum = 0;
int tiles_len = (M + TILE_SIZE - 1) / TILE_SIZE;
for (int tile = 0; tile < tiles_len; tile++) {
int aCol = tile * TILE_SIZE + tx;
int bRow = tile * TILE_SIZE + ty;
if (aCol < M) {
sA[ty][tx] = A[row * M + aCol];
} else {
sA[ty][tx] = 0;
}
sB[ty][tx] = (T)((uint64_t)B[bRow * K + col] & ((uint64_t)(bRow >= M) - 1));
__syncthreads();
for (int k = 0; k < TILE_SIZE; k++) {
sum += sA[ty][k] * sB[k][tx];
}
}
C[row * K + col] = sum;
}
template <typename T>
__global__ void dumb_mat_mul(T *A, T *B, T *C, int N, int M, int K) {
int col = blockIdx.x * blockDim.x + threadIdx.x;
int row = blockIdx.y * blockDim.y + threadIdx.y;
if (col >= K || row >= M) return;
T sum = 0;
for (int i = 0; i < M; i++) {
sum += A[row * M + i] * B[i * K + col];
}
C[row * K + col] = sum;
}
#define N 1024
#define M 1024
#define K 1024
#define NO_PRINT 1
#define GRID_DIM 1
#define BLOCK_DIM 32
#define MAT_TYPE int
#define MAT_FMT "%d\t"
#define A_LEN (N * M)
#define B_LEN (M * K)
#define C_LEN (N * K)
#define A_SIZE (sizeof(MAT_TYPE) * N * M)
#define B_SIZE (sizeof(MAT_TYPE) * M * K)
#define C_SIZE (sizeof(MAT_TYPE) * N * K)
#include <cstdio>
#include <random>
#include <chrono>
using namespace std::chrono;
template <typename T>
void mat_print(T *a, const char *fmt, int n, int m) {
for (auto row = 0; row < n; row++) {
for (auto col = 0; col < m; col++) {
printf(fmt, a[row * m + col]);
}
printf("\n");
}
}
int main() {
std::random_device rd;
std::mt19937 engine(rd());
std::uniform_int_distribution<MAT_TYPE> dist(1, 10);
auto buf = (MAT_TYPE *)malloc(A_SIZE + B_SIZE + C_SIZE);
for (auto i = 0; i < A_LEN + B_LEN; i++) {
buf[i] = dist(engine);
}
MAT_TYPE *a = buf;
MAT_TYPE *b = a + A_LEN;
MAT_TYPE *c = b + B_LEN;
#if NO_PRINT==0
printf("\na\n");
mat_print(a, MAT_FMT, N, M);
printf("\nb\n");
mat_print(b, MAT_FMT, M, K);
#endif
MAT_TYPE *d_a, *d_b, *d_c;
cudaMalloc(&d_a, A_SIZE);
cudaMalloc(&d_b, B_SIZE);
cudaMalloc(&d_c, C_SIZE);
cudaMemcpy(d_a, a, A_SIZE, cudaMemcpyHostToDevice);
cudaMemcpy(d_b, b, B_SIZE, cudaMemcpyHostToDevice);
dim3 gridDim(GRID_DIM, GRID_DIM);
dim3 blockDim(BLOCK_DIM, BLOCK_DIM);
int cycles = 0;
microseconds duration(0);
while (duration.count() < 1e6) {
auto start = high_resolution_clock::now();
mat_mul<MAT_TYPE, BLOCK_DIM><<<gridDim, blockDim>>>(d_a, d_b, d_c, N, M, K);
cudaDeviceSynchronize();
auto end = high_resolution_clock::now();
cycles++;
duration += duration_cast<microseconds>(end - start);
}
#if NO_PRINT==0
cudaMemcpy(c, d_c, C_SIZE, cudaMemcpyDeviceToHost);
printf("\nc\n");
mat_print(c, MAT_FMT, N, K);
#endif
printf("optimized mul take %f usec avg in %d cycles\n", (float)(duration.count()) / cycles, cycles);
cycles = 0;
duration = microseconds(0);
while (duration.count() < 1e6) {
auto start = high_resolution_clock::now();
dumb_mat_mul<MAT_TYPE><<<gridDim, blockDim>>>(d_a, d_b, d_c, N, M, K);
cudaDeviceSynchronize();
auto end = high_resolution_clock::now();
cycles++;
duration += duration_cast<microseconds>(end - start);
}
#if NO_PRINT==0
cudaMemcpy(c, d_c, C_SIZE, cudaMemcpyDeviceToHost);
printf("\nc\n");
mat_print(c, MAT_FMT, N, K);
#endif
printf("dumb mul take %f usec avg in %d cycles\n", (float)(duration.count()) / cycles, cycles);
cudaFree(a);
cudaFree(b);
cudaFree(c);
free(buf);
}

27
5/data science/1e/main.py Normal file
View File

@ -0,0 +1,27 @@
import sys, time, math
import numpy as np
import cupy as cp
def measure(a, b):
duration = 0
cycles = 0
while (duration < 1):
start = time.perf_counter()
c = a @ b
cp.cuda.Stream.null.synchronize()
end = time.perf_counter()
duration += end - start
cycles += 1
return duration / cycles
n = 1024
a = np.random.rand(n, n).astype(np.float32)
b = np.random.rand(n, n).astype(np.float32)
print('numpy take', measure(a, b) * 1e6, 'usec')
a = cp.random.rand(n, n, dtype = cp.float32)
b = cp.random.rand(n, n, dtype = cp.float32)
print('cupy take', measure(a, b) * 1e6, 'usec')

171
5/data science/1e/op.ptx Normal file
View File

@ -0,0 +1,171 @@
.version 8.4
.target sm_75
.address_size 64
.visible .func add_u16(
.param .b64 out_c,
.param .align 16 .b8 in_a[16],
.param .align 16 .b8 in_b[16]
) {
.reg .u64 %ra<2>, %rb<2>;
.reg .b64 %rdc;
ld.param.b64 %rdc, [out_c];
ld.param.v2.u64 {%ra1, %ra0}, [in_a];
ld.param.v2.u64 {%rb1, %rb0}, [in_b];
add.cc.u64 %ra0, %ra0, %rb0;
addc.u64 %ra1, %ra1, %rb1;
st.v2.u64 [%rdc], {%ra1, %ra0};
ret;
}
.visible .func sub_u16(
.param .b64 out_c,
.param .align 16 .b8 in_a[16],
.param .align 16 .b8 in_b[16]
) {
.reg .u64 %ra<2>, %rb<2>;
.reg .b64 %rdc;
ld.param.b64 %rdc, [out_c];
ld.param.v2.u64 {%ra1, %ra0}, [in_a];
ld.param.v2.u64 {%rb1, %rb0}, [in_b];
sub.cc.u64 %ra0, %ra0, %rb0;
subc.u64 %ra1, %ra1, %rb1;
st.v2.u64 [%rdc], {%ra1, %ra0};
ret;
}
.visible .func add_u32(
.param .b64 out_c,
.param .align 16 .b8 in_a[32],
.param .align 16 .b8 in_b[32]
) {
.reg .u64 %ra<4>, %rb<4>;
.reg .b64 %rdc;
ld.param.b64 %rdc, [out_c];
ld.param.v2.u64 {%ra3, %ra2}, [in_a];
ld.param.v2.u64 {%ra1, %ra0}, [in_a + 16];
ld.param.v2.u64 {%rb3, %rb2}, [in_b];
ld.param.v2.u64 {%rb1, %rb0}, [in_b + 16];
add.cc.u64 %ra0, %ra0, %rb0;
addc.cc.u64 %ra1, %ra1, %rb1;
addc.cc.u64 %ra2, %ra2, %rb2;
addc.u64 %ra3, %ra3, %rb3;
st.v2.u64 [%rdc], {%ra3, %ra2};
st.v2.u64 [%rdc + 16], {%ra1, %ra0};
ret;
}
.visible .func sub_u32(
.param .b64 out_c,
.param .align 16 .b8 in_a[32],
.param .align 16 .b8 in_b[32]
) {
.reg .u64 %ra<4>, %rb<4>;
.reg .b64 %rdc;
ld.param.b64 %rdc, [out_c];
ld.param.v2.u64 {%ra3, %ra2}, [in_a];
ld.param.v2.u64 {%ra1, %ra0}, [in_a + 16];
ld.param.v2.u64 {%rb3, %rb2}, [in_b];
ld.param.v2.u64 {%rb1, %rb0}, [in_b + 16];
sub.cc.u64 %ra0, %ra0, %rb0;
subc.cc.u64 %ra1, %ra1, %rb1;
subc.cc.u64 %ra2, %ra2, %rb2;
subc.u64 %ra3, %ra3, %rb3;
st.v2.u64 [%rdc], {%ra3, %ra2};
st.v2.u64 [%rdc + 16], {%ra1, %ra0};
ret;
}
.visible .func mul_lo_u16(
.param .b64 out_c,
.param .align 16 .b8 in_a[16],
.param .align 16 .b8 in_b[16]
) {
.reg .u64 %a, %b, %c, %d, %a_b, %c_d;
.reg .u64 %ac, %bd_hi, %bd_lo, %p;
.reg .b64 %rdc;
ld.param.b64 %rdc, [out_c];
ld.param.v2.u64 {%a, %b}, [in_a];
ld.param.v2.u64 {%c, %d}, [in_b];
mul.lo.u64 %ac, %a, %c;
mul.lo.u64 %bd_lo, %b, %d;
mul.hi.u64 %bd_hi, %b, %d;
add.u64 %a_b, %a, %b;
add.u64 %c_d, %c, %d;
mul.lo.u64 %p, %a_b, %c_d;
sub.u64 %p, %p, %ac;
sub.u64 %p, %p, %bd_lo;
add.u64 %p, %p, %bd_hi;
st.v2.u64 [%rdc], {%p, %bd_lo};
ret;
}
.visible .func mul_u16(
.param .b64 out_c_hi,
.param .b64 out_c_lo,
.param .align 16 .b8 in_a[16],
.param .align 16 .b8 in_b[16]
) {
.reg .u64 %a, %b, %c, %d;
.reg .u64 %a_b_hi, %a_b_lo, %c_d_hi, %c_d_lo;
.reg .u64 %p_hi, %p_lo, %p_hi2, %p_lo2;
.reg .u64 %ac_hi, %ac_lo, %bd_hi, %bd_lo;
.reg .b64 %rdc_hi, %rdc_lo;
ld.param.b64 %rdc_hi, [out_c_hi];
ld.param.b64 %rdc_lo, [out_c_lo];
ld.param.v2.u64 {%a, %b}, [in_a];
ld.param.v2.u64 {%c, %d}, [in_b];
mul.lo.u64 %ac_lo, %a, %c;
mul.hi.u64 %ac_hi, %a, %c;
mul.lo.u64 %bd_lo, %b, %d;
mul.hi.u64 %bd_hi, %b, %d;
add.cc.u64 %a_b_lo, %a, %b;
addc.u64 %a_b_hi, %a, %b;
add.cc.u64 %c_d_lo, %c, %d;
addc.u64 %c_d_hi, %c, %d;
mul.lo.u64 %p_lo, %a_b_lo, %c_d_lo;
mul.hi.u64 %p_hi, %a_b_lo, %c_d_lo;
mul.lo.u64 %p_hi2, %a_b_hi, %c_d_hi;
st.v2.u64 [%rdc_lo], {%p_hi, %p_lo};
st.v2.u64 [%rdc_hi], {%a_b_lo, %p_hi2};
ret;
}

View File

@ -0,0 +1,205 @@
#include <stdio.h>
#include <stdint.h>
extern "C" __device__ void add_u16(
ulonglong2 *out_c,
ulonglong2 in_a,
ulonglong2 in_b
);
extern "C" __device__ void sub_u16(
ulonglong2 *out_c,
ulonglong2 in_a,
ulonglong2 in_b
);
extern "C" __device__ void add_u32(
ulonglong4 *out_c,
ulonglong4 in_a,
ulonglong4 in_b
);
extern "C" __device__ void sub_u32(
ulonglong4 *out_c,
ulonglong4 in_a,
ulonglong4 in_b
);
extern "C" __device__ void mul_lo_u16(
ulonglong2 *out_c,
ulonglong2 in_a,
ulonglong2 in_b
);
extern "C" __device__ void mul_u16(
ulonglong2 *out_c_hi,
ulonglong2 *out_c_lo,
ulonglong2 in_a,
ulonglong2 in_b
);
__device__ bool equ_u16(ulonglong2 a, ulonglong2 b) {
return a.x == b.x && a.y == b.y;
}
__device__ bool equ_u32(ulonglong4 a, ulonglong4 b) {
return a.x == b.x &&
a.y == b.y &&
a.z == b.z &&
a.w == b.w;
}
__device__ int cmp_u32(ulonglong4 a, ulonglong4 b) {
if (a.x < b.x)
return -1;
else if (a.x > b.x)
return 1;
if (a.y < b.y)
return -1;
else if (a.y > b.y)
return 1;
if (a.z < b.z)
return -1;
else if (a.z > b.z)
return 1;
if (a.w < b.w)
return -1;
else if (a.w > b.w)
return 1;
return 0;
}
__device__ void mul_lo_u32(
ulonglong4 *out_c,
ulonglong4 in_a,
ulonglong4 in_b
) {
auto a = (ulonglong2 *)&in_a.x;
auto b = (ulonglong2 *)&in_a.z;
auto c = (ulonglong2 *)&in_b.x;
auto d = (ulonglong2 *)&in_b.z;
ulonglong2 a_b, c_d, ac, bd_hi, bd_lo, p;
mul_lo_u16(&ac, *a, *c);
mul_u16(&bd_hi, &bd_lo, *b, *d);
add_u16(&a_b, *a, *b);
add_u16(&c_d, *c, *d);
mul_lo_u16(&p, a_b, c_d);
sub_u16(&p, p, ac);
sub_u16(&p, p, bd_lo);
add_u16(&p, p, bd_hi);
out_c->x = p.x;
out_c->y = p.y;
out_c->z = bd_lo.x;
out_c->w = bd_lo.y;
}
__device__ void print_u16(ulonglong2 a) {
printf("0x%016llx.%016llx\n", a.x, a.y);
}
__device__ void print_u32(ulonglong4 a) {
printf("0x%016llx.%016llx.%016llx.%016llx\n", a.x, a.y, a.z, a.w);
}
#define U8_MAX 0xFFFFFFFFFFFFFFFF
#define U16_MAX {U8_MAX, U8_MAX}
#define U32_MAX {U8_MAX, U8_MAX, U8_MAX, U8_MAX}
__global__ void test(bool *passed) {
*passed = true;
{
ulonglong4 a = U32_MAX;
ulonglong4 b = {0, 0, 0, 1};
ulonglong4 c = {0, 0, 0, 0};
add_u32(&a, a, b);
if (!equ_u32(a, c)) {
printf("add_u32\n");
print_u32(a);
*passed = false;
}
}
{
ulonglong4 a = {0, 0, 0, 0};
ulonglong4 b = {0, 0, 0, 1};
ulonglong4 c = U32_MAX;
sub_u32(&a, a, b);
if (!equ_u32(a, c)) {
printf("sub_u32\n");
print_u32(a);
*passed = false;
}
}
{
ulonglong2 a = U16_MAX;
ulonglong2 b = {0, U8_MAX};
ulonglong2 c = {U8_MAX, 1};
mul_lo_u16(&a, a, b);
if (!equ_u16(a, c)) {
printf("mul_lo_u16\n");
print_u16(a);
*passed = false;
}
}
{
ulonglong2 a = U16_MAX;
ulonglong2 b = {0, U8_MAX};
ulonglong2 c_hi = {0, U8_MAX - 1};
ulonglong2 c_lo = {U8_MAX, 1};
mul_u16(&a, &b, a, b);
if (!equ_u16(a, c_hi) || !equ_u16(b, c_lo)) {
printf("mul_u16\n");
print_u16(a);
print_u16(b);
*passed = false;
}
a = U16_MAX;
b = U16_MAX;
c_hi = {U8_MAX, U8_MAX - 1};
c_lo = {0, 1};
mul_u16(&a, &b, a, b);
if (!equ_u16(a, c_hi) || !equ_u16(b, c_lo)) {
printf("mul_u16\n");
print_u16(a);
print_u16(b);
*passed = false;
}
}
{
ulonglong4 a = U32_MAX;
ulonglong4 b = {0, 0, U8_MAX, U8_MAX};
ulonglong4 c = {U8_MAX, U8_MAX, 0, 1};
mul_lo_u32(&a, a, b);
if (!equ_u32(a, c)) {
printf("mul_lo_u32\n");
print_u32(a);
*passed = false;
}
}
}
int main() {
bool test_passed, *d_test_passed;
cudaMalloc(&d_test_passed, sizeof(bool));
test<<<1, 1>>>(d_test_passed);
cudaDeviceSynchronize();
cudaMemcpy(&test_passed, d_test_passed, sizeof(bool), cudaMemcpyDeviceToHost);
cudaFree(d_test_passed);
if (!test_passed) {
printf("test not passed\n");
return 1;
}
return 0;
}

22
5/data science/1e/test.py Normal file
View File

@ -0,0 +1,22 @@
U8_MAX = 0xFFFFFFFFFFFFFFFF
U16_MAX = U8_MAX << 64 | U8_MAX
U32_MAX = U16_MAX << 128 | U16_MAX
def dothex(num):
strhex = hex(num)[2:]
dothex = strhex[-16:]
strhex = strhex[:-16]
while len(strhex) > 0:
dothex = strhex[-16:] + '.' + dothex
strhex = strhex[:-16]
return '0x' + dothex
print('mul_u16', dothex((U16_MAX * U8_MAX >> 128) %
(U16_MAX + 1)), dothex(U16_MAX * U8_MAX % (U16_MAX + 1)))
print('mul_u16', dothex((U16_MAX * U16_MAX >> 128) %
(U16_MAX + 1)), dothex(U16_MAX * U16_MAX % (U16_MAX + 1)))
print('mul_lo_u32', dothex(U32_MAX * U16_MAX % (U32_MAX + 1)))
print('div_lo_u32', dothex(U32_MAX // U8_MAX), dothex(U32_MAX - U32_MAX // U8_MAX))