난수: typeshade/random
typeshade/random은 두 번째 공용 모듈이다(규칙 11.20). 커널이 난수를 뽑는 생성기와 해시, 호스트가 부르는 채우기 함수가 들어 있습니다. 모두 모든 tier에서 같은 비트를 냅니다.
1. 디바이스 인터페이스
"use typeshade" 파일은 스칼라 헬퍼 세 개를 가져와 자기 커널이나 진입점에서 부릅니다. 예제의 난수는 시드와 인덱스에만 달려 있으므로, tier나 디스패치 모양이 바뀌어도 그대로입니다.
"use typeshade";import { xorshift32, hash32, uniform } from "typeshade/random";
export function jitter(out: array<f32>, seed: u32): void { for (let i: u32 = 0; i < out.length; i++) { out[i] = uniform(hash32(seed ^ i)); }}| 함수 | 하는 일 |
|---|---|
xorshift32(state: u32): u32 | xorshift32를 한 단계 진행합니다. 상태가 영이면 계속 영이므로, 영이 아닌 값으로 시드를 줍니다. |
hash32(x: u32): u32 | 32비트 정수 마무리 해시이며, 상태 없이 인덱스에서 난수를 뽑을 때 씁니다. hash32(0)은 0입니다. |
uniform(word: u32): f32 | word의 상위 24비트를 [0, 1) 범위의 f32로 바꿉니다. 이 값은 f32에서 정확하므로, 같은 워드는 모든 tier에서 같은 수가 됩니다. |
2. 호스트 인터페이스
호스트 코드도 같은 하위 경로를 가져옵니다. 세 헬퍼는 CPU에서 도는 함수로 여기에도 있고, 그 옆에 함수 세 개가 더 있습니다. generator(seed)는 같은 hash32와 uniform을 씁니다. 그래서 같은 시드라면 호스트에서 섞은 결과와 디바이스에서 뽑은 난수가 비트 단위로 맞습니다.
import { fillUniform, generator } from 'typeshade/random';import { resident } from 'typeshade/runtime';
const noise = resident(1024, 'f32');fillUniform(noise, 12345); // element i is uniform(hash32(12345 ^ i))const draw = generator(12345); // the same draws on the hostconst first = draw();| 함수 | 하는 일 |
|---|---|
fillUniform(out, seed: u32) | 커널 함수입니다. 원소 i는 uniform(hash32(seed ^ i))이며, [0, 1) 범위의 난수입니다. |
fillWords(out: array<u32>, seed: u32) | 커널 함수입니다. 원소 i는 hash32(seed ^ i)이입니다. |
generator(seed: u32): () => number | 평범한 JavaScript 함수입니다. 부를 때마다 0에서 시작하는 카운터의 다음 난수를 줍니다. |
3. 검증 방식
컴파일러의 테스트는 CPU tier의 fillUniform(out, seed)를 원소마다 generator(seed)와 비교합니다. 차등 게이트는 WebGPU와 WebGL2에서 두 채우기 함수를 CPU 오라클과 비교합니다.
4. 아직 없는 것
정규 분포, Philox나 PCG처럼 상태가 더 큰 카운터 기반 생성기, 순열은 다음에 나옵니다.