cargo 옵션

  • cargo new {folder_path} 새 프로젝트를 생성할 수 있습니다.
  • cargo build 프로젝트를 빌드할 수 있습니다.
  • cargo run 한 번에 프로젝트를 빌드하고 실행할 수 있습니다.
  • cargo check 바이너리를 생성하지 않고 프로젝트의 에러를 체크할 수 있습니다.
  • cargo build --release 최종 배포를 위한 릴리즈를 빌드합니다.
  • cargo update Cargo.lock 파일을 무시하고 Cargo.toml로부터 최신 버전의 크레이트를 다시 설치함.
  • cargo doc --open 현재 프로젝트에서 사용되는 크레이트들의 API를 문서를 다운로드 받고 웹브라우저에서 표시해줌.

WebGPU를 이용한 계산(Computing)

3개의 실수값을 전달하고 이 값에 2를 곱한 결과를 GPU를 통해 병렬로 처리 실행해 그 결과를 얻는 코드입니다. 먼저 GPU에 대한 객체를 얻는 것은 비동기로 처리해야 하므로 다음과 같은 코드가 필요합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
async function main() {
// 여기에 이후 모든 코드가 작성됩니다.
}
await main();
async function main() { // 여기에 이후 모든 코드가 작성됩니다. } await main();
async function main() {
  // 여기에 이후 모든 코드가 작성됩니다.
}

await main();

자, 이제 GPU에 대한 객체를 얻습니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const adapter = await navigator.gpu.requestAdapter();
const device = await adapter.requestDevice();
const adapter = await navigator.gpu.requestAdapter(); const device = await adapter.requestDevice();
  const adapter = await navigator.gpu.requestAdapter();
  const device = await adapter.requestDevice();

실행할 쉐이더 코드를 작성합니다. 쉐이더 코드는 WGSL로 작성됩니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const module = device.createShaderModule({
label: 'doubling compute module',
code: /* wgsl */ `
@group(0) @binding(0) var<storage, read_write> data: array<f32>;
@compute @workgroup_size(1) fn computeSomething(
@builtin(global_invocation_id) id: vec3u
) {
let i = id.x;
data[i] = data[i] * 2.0;
}
`,
});
const module = device.createShaderModule({ label: 'doubling compute module', code: /* wgsl */ ` @group(0) @binding(0) var<storage, read_write> data: array<f32>; @compute @workgroup_size(1) fn computeSomething( @builtin(global_invocation_id) id: vec3u ) { let i = id.x; data[i] = data[i] * 2.0; } `, });
  const module = device.createShaderModule({
    label: 'doubling compute module',
    code: /* wgsl */ `
      @group(0) @binding(0) var<storage, read_write> data: array<f32>;
 
      @compute @workgroup_size(1) fn computeSomething(
        @builtin(global_invocation_id) id: vec3u
      ) {
        let i = id.x;
        data[i] = data[i] * 2.0;
      }
    `,
  });

위의 쉐이더 코드를 실행할 파이프라인 객체를 생성합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const pipeline = device.createComputePipeline({
label: 'x2 compute pipeline',
layout: 'auto',
compute: {
module,
entryPoint: 'computeSomething', // 실행할 쉐이더 함수
},
});
const pipeline = device.createComputePipeline({ label: 'x2 compute pipeline', layout: 'auto', compute: { module, entryPoint: 'computeSomething', // 실행할 쉐이더 함수 }, });
  const pipeline = device.createComputePipeline({
    label: 'x2 compute pipeline',
    layout: 'auto',
    compute: {
      module,
      entryPoint: 'computeSomething', // 실행할 쉐이더 함수
    },
  });

실행할 쉐이더 함수를 보면 1개의 인자를 받는데, 그 인자를 정의합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const input = new Float32Array([1, 3, 5]);
const input = new Float32Array([1, 3, 5]);
  const input = new Float32Array([1, 3, 5]);

위의 인자는 CPU 메모리에 있으므로 이를 GPU 메모리에 만들어서 복사해 줘야 합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const workBuffer = device.createBuffer({
label: 'work buffer',
size: input.byteLength,
usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
});
device.queue.writeBuffer(workBuffer, 0, input);
const workBuffer = device.createBuffer({ label: 'work buffer', size: input.byteLength, usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST, }); device.queue.writeBuffer(workBuffer, 0, input);
  const workBuffer = device.createBuffer({
    label: 'work buffer',
    size: input.byteLength,
    usage: GPUBufferUsage.STORAGE | GPUBufferUsage.COPY_SRC | GPUBufferUsage.COPY_DST,
  });

  device.queue.writeBuffer(workBuffer, 0, input);

GPU를 통해 계산된 최종 결과를 읽기 위한 사본 버퍼를 생성합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const resultBuffer = device.createBuffer({
label: 'result buffer',
size: input.byteLength,
usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
});
const resultBuffer = device.createBuffer({ label: 'result buffer', size: input.byteLength, usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST });
  const resultBuffer = device.createBuffer({
    label: 'result buffer',
    size: input.byteLength,
    usage: GPUBufferUsage.MAP_READ | GPUBufferUsage.COPY_DST
  });

계산을 위해 사용될 인자에 대한 버퍼가 무엇인지를 명확히 지정합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const bindGroup = device.createBindGroup({
label: 'bindGroup for work buffer',
layout: pipeline.getBindGroupLayout(0),
entries: [
{ binding: 0, resource: { buffer: workBuffer } },
],
});
const bindGroup = device.createBindGroup({ label: 'bindGroup for work buffer', layout: pipeline.getBindGroupLayout(0), entries: [ { binding: 0, resource: { buffer: workBuffer } }, ], });
  const bindGroup = device.createBindGroup({
    label: 'bindGroup for work buffer',
    layout: pipeline.getBindGroupLayout(0),
    entries: [
      { binding: 0, resource: { buffer: workBuffer } },
    ],
  });

GPU에서 실행할 명령을 인코딩합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const encoder = device.createCommandEncoder({ label: 'x2 encoder' });
const pass = encoder.beginComputePass({ label: 'x2 compute pass' });
pass.setPipeline(pipeline);
pass.setBindGroup(0, bindGroup);
pass.dispatchWorkgroups(input.length);
pass.end();
const encoder = device.createCommandEncoder({ label: 'x2 encoder' }); const pass = encoder.beginComputePass({ label: 'x2 compute pass' }); pass.setPipeline(pipeline); pass.setBindGroup(0, bindGroup); pass.dispatchWorkgroups(input.length); pass.end();
  const encoder = device.createCommandEncoder({ label: 'x2 encoder' });
  const pass = encoder.beginComputePass({ label: 'x2 compute pass' });

  pass.setPipeline(pipeline);
  pass.setBindGroup(0, bindGroup);
  pass.dispatchWorkgroups(input.length);
  pass.end();

계산 결과를 읽어 내기 위해 맵핑용 버퍼에 복사하는 명령을 인코딩합니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
encoder.copyBufferToBuffer(workBuffer, 0, resultBuffer, 0, resultBuffer.size);
encoder.copyBufferToBuffer(workBuffer, 0, resultBuffer, 0, resultBuffer.size);
  encoder.copyBufferToBuffer(workBuffer, 0, resultBuffer, 0, resultBuffer.size);

실해 명령의 실행은 다음 코드를 통해 이루어집니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
const commandBuffer = encoder.finish();
device.queue.submit([commandBuffer]);
const commandBuffer = encoder.finish(); device.queue.submit([commandBuffer]);
   const commandBuffer = encoder.finish();
   device.queue.submit([commandBuffer]);

실행 결과를 콘솔에 출력하기 위한 코드입니다.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
await resultBuffer.mapAsync(GPUMapMode.READ);
const result = new Float32Array(resultBuffer.getMappedRange());
console.log('input', input);
console.log('result', result);
resultBuffer.unmap();
await resultBuffer.mapAsync(GPUMapMode.READ); const result = new Float32Array(resultBuffer.getMappedRange()); console.log('input', input); console.log('result', result); resultBuffer.unmap();
  await resultBuffer.mapAsync(GPUMapMode.READ);
  const result = new Float32Array(resultBuffer.getMappedRange());
 
  console.log('input', input);
  console.log('result', result);
 
  resultBuffer.unmap();

실행 결과를 보면 다음과 같습니다.

실행 결과를 보면 입력값에 2배만큼 곱해졌는데, 이는 GPU를 통해 병렬로 계산된 것입니다.