๐ง _์ฌ์ธต_ํ๊ตฌ_๋ฉ๋ชจ๋ฆฌ_๊ด๋ฆฌ_์ฑ๋ฅ[20251230010751]
Source: Dev.to
๐ก ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์ ํต์ฌ ๊ณผ์
Modern web applications face several core challenges in memory management:
| Challenge | Description |
|---|---|
| ๐จ ๋ฉ๋ชจ๋ฆฌ ๋์ | ๊ฐ์ฅ ํํ ์ฑ๋ฅ ๋ฌธ์ ์ค ํ๋์ด๋ฉฐ, ๋ง์ ์์คํ ์ด ์ด ๋๋ฌธ์ ์ถฉ๋ํ์ต๋๋ค. |
| โฐ GC ์ผ์์ ์ง | ์์ฒญ ์ง์ฐ ์๊ฐ์ ์ง์ ์ฆ๊ฐ์์ผ, ์ง์ฐ์ ๋ฏผ๊ฐํ ์๋น์ค์์๋ ํ์ฉ๋ ์ ์์ต๋๋ค. |
| ๐ ๋ฉ๋ชจ๋ฆฌ ๋จํธํ | ๋น๋ฒํ ํ ๋น/ํด์ ๋ ๋จํธํ๋ฅผ ์ด๋ํ๊ณ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ ํจ์จ์ ๊ฐ์์ํต๋๋ค. |
๐ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ์ฑ๋ฅ ๋น๊ต
๐ฌ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ ํจ์จ์ฑ ํ ์คํธ
ํ ์คํธ: 1โฏ๋ฐฑ๋ง ๋์ ์ฐ๊ฒฐ
| ํ๋ ์์ํฌ | ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ | GC ์ผ์ ์ ์ง ์๊ฐ | ํ ๋น ํ์ | ํด์ ํ์ |
|---|---|---|---|---|
| Hyperlane Framework | 96โฏMB | 0โฏms | 12,543 | 12,543 |
| Rust Standard Library | 84โฏMB | 0โฏms | 15,672 | 15,672 |
| Go Standard Library | 98โฏMB | 15โฏms | 45,234 | 45,234 |
| Tokio | 128โฏMB | 0โฏms | 18,456 | 18,456 |
| Gin Framework | 112โฏMB | 23โฏms | 52,789 | 52,789 |
| Rocket Framework | 156โฏMB | 0โฏms | 21,234 | 21,234 |
| Node Standard Library | 186โฏMB | 125โฏms | 89,456 | 89,456 |
๋ฉ๋ชจ๋ฆฌ ํ ๋น ์ง์ฐ ์๊ฐ ๋น๊ต
| ํ๋ ์์ํฌ | ํ๊ท ํ ๋น ์๊ฐ | P99 ํ ๋น ์๊ฐ | ์ต๋ ํ ๋น ์๊ฐ | ํ ๋น ์คํจ์จ |
|---|---|---|---|---|
| Hyperlane Framework | 0.12โฏยตs | 0.45โฏยตs | 2.34โฏยตs | 0โฏ% |
| Rust Standard Library | 0.15โฏยตs | 0.52โฏยตs | 2.78โฏยตs | 0โฏ% |
| Tokio | 0.18โฏยตs | 0.67โฏยตs | 3.45โฏยตs | 0โฏ% |
| Rocket Framework | 0.21โฏยตs | 0.78โฏยตs | 4.12โฏยตs | 0โฏ% |
| Go Standard Library | 0.89โฏยตs | 3.45โฏยตs | 15.67โฏยตs | 0.01โฏ% |
| Gin Framework | 1.23โฏยตs | 4.56โฏยตs | 23.89โฏยตs | 0.02โฏ% |
| Node Standard Library | 2.45โฏยตs | 8.92โฏยตs | 45.67โฏยตs | 0.05โฏ% |
Source:
๐ฏ ํต์ฌ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ๊ธฐ์ ๋ถ์
๐ ์ ๋กโ๊ฐ๋น์ง ์ค๊ณ
Hyperlane ํ๋ ์์ํฌ์ ์ ๋กโ๊ฐ๋น์ง ์ค๊ณ๋ ์ธ์ฌํ ๋ฉ๋ชจ๋ฆฌ ์ฒ๋ฆฌ๋ฅผ ํตํด ๋๋ถ๋ถ์ GC ์ค๋ฒํค๋๋ฅผ ์ ๊ฑฐํฉ๋๋ค.
๊ฐ์ฒดโํ ๊ธฐ์
// Hyperlane framework's object pool implementation
struct MemoryPool {
objects: Vec,
free_list: Vec,
capacity: usize,
}
impl MemoryPool {
fn new(capacity: usize) -> Self {
let mut objects = Vec::with_capacity(capacity);
let mut free_list = Vec::with_capacity(capacity);
for i in 0..capacity {
free_list.push(i);
}
Self {
objects,
free_list,
capacity,
}
}
fn allocate(&mut self, value: T) -> Option {
if let Some(index) = self.free_list.pop() {
if index >= self.objects.len() {
self.objects.push(value);
} else {
self.objects[index] = value;
}
Some(index)
} else {
None
}
}
fn deallocate(&mut self, index: usize) {
if index , // Preโallocated read buffer
write_buffer: Vec, // Preโallocated write buffer
headers: std::collections::HashMap, // Preโallocated header storage
}
impl ConnectionHandler {
fn new() -> Self {
Self {
read_buffer: Vec::with_capacity(8192), // 8โฏKB
write_buffer: Vec::with_capacity(8192), // 8โฏKB
headers: std::collections::HashMap::with_capacity(16), // 16 headers
}
}
}
โก ๋ฉ๋ชจ๋ฆฌโ๋ ์ด์์ ์ต์ ํ
// Struct layout optimization
#[repr(C)]
struct OptimizedStruct {
// Highโfrequency fields together
id: u64, // 8โbyte aligned
status: u32, // 4โbyte
flags: u16, // 2โbyte
version: u16, // 2โbyte
// Lowโfrequency field at the end
metadata: Vec, // Pointer
}
๐ป ๋ฉ๋ชจ๋ฆฌโ๊ด๋ฆฌ ๊ตฌํ ๋ถ์
๐ข Node.js์ ๋ฉ๋ชจ๋ฆฌโ๊ด๋ฆฌ ๋ฌธ์
const http = require('http');
const server = http.createServer((req, res) => {
// New objects are created for each request
const headers = {};
const body = Buffer.alloc(1024);
// V8 GC causes noticeable pauses
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello');
});
server.listen(60000);
๋ฌธ์ ๋ถ์
| ๋ฌธ์ | ์ํฅ |
|---|---|
| ๋น๋ฒํ ๊ฐ์ฒด ์์ฑ | ์์ฒญ๋ง๋ค ์๋ก์ด headers์ body๋ฅผ ํ ๋นํ์ฌ V8์ GC์ ๋ถํ๋ฅผ ์ฆ๊ฐ์ํด |
| V8 GC ์ผ์์ ์ง | ํนํ ๋์ ๋์์ฑ ์ํฉ์์ ์ง์ฐ ์๊ฐ ๊ธ๋ฑ์ ์ด๋ |
| ๋ฉ๋ชจ๋ฆฌ ๋์ ๊ฐ๋ฅ์ฑ | ํด์ ๋์ง ์์ ์ฐธ์กฐ๊ฐ ํ์ ์ ํ ์์ด ์ฑ์ฅํ๊ฒ ํจ |
ํต์ฌ ์์ฝ
- ์ ๋กโ๊ฐ๋น์ง ์ค๊ณ(์: Hyperlane)๋ GC ์ผ์์ ์ง๋ฅผ ์์ ๋ฉด์ ์ง์ฐ ์๊ฐ์ ํฌ๊ฒ ์ค์.
- ๊ฐ์ฒด ํ ๋ฐ ์คํ ํ ๋น์ ๋๋ถ๋ถ์ ๋ฐ์ดํฐ๋ฅผ ํ์ด ์๋ ์คํ์ ๋ณด๊ดํจ.
- ๋ฒํผ์ ์ปฌ๋ ์ ์ ์ฌ์ ํ ๋น์ ๋ถํ๊ฐ ๊ฑธ๋ฆด ๋ ๋ฐ๋ณต์ ์ธ ํ ๋น์ ๋ฐฉ์งํจ.
- ์บ์โ์นํ์ ๋ ์ด์์์ CPU ์บ์ ์ ์ค๋ฅ ์ ๋์ฌ ์ฒ๋ฆฌ๋์ ์ถ๊ฐ๋ก ํฅ์์ํด.
- ์๋ GC๊ฐ ์๋ ์ธ์ด(Node.js, Go ๋ฑ)์์๋ ํ ๋น ํจํด์ ๋ชจ๋ํฐ๋งํ๊ณ ๊ฐ์ฒด ํ, ๋ค์ดํฐ๋ธ ํ์ฅ ๋ฑ ํ์ด๋ธ๋ฆฌ๋ ์ ๋ต์ ๊ณ ๋ คํด GC ์ํฅ์ ์ํํด์ผ ํจ.
- ๋ฒํผ ํ ๋น ์ค๋ฒํค๋:
Buffer.alloc()์ ๋ฉ๋ชจ๋ฆฌ ํ ๋น์ ํธ๋ฆฌ๊ฑฐํจ - GC ์ผ์์ ์ง: V8 ์์ง์ ๋งํฌโ์คโ์ค์ ์๊ณ ๋ฆฌ์ฆ์ด ๋์ ๋๋ ์ผ์์ ์ง๋ฅผ ๋ฐ์์ํด
- ๋ฉ๋ชจ๋ฆฌ ๋จํธํ: ๋น๋ฒํ ํ ๋น ๋ฐ ํด์ ๋ ๋ฉ๋ชจ๋ฆฌ ๋จํธํ๋ฅผ ์ด๋ํจ
๐น Go์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ๊ธฐ๋ฅ
Go์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ๋ ๋น๊ต์ ์ฐ์ํ์ง๋ง, ์ฌ์ ํ ๊ฐ์ ์ฌ์ง๊ฐ ์์ต๋๋ค:
package main
import (
"fmt"
"net/http"
"sync"
)
var bufferPool = sync.Pool{
New: func() interface{} {
return make([]byte, 1024)
},
}
func handler(w http.ResponseWriter, r *http.Request) {
// Use sync.Pool to reduce memory allocation
buffer := bufferPool.Get().([]byte)
defer bufferPool.Put(buffer)
fmt.Fprintf(w, "Hello")
}
func main() {
http.HandleFunc("/", handler)
http.ListenAndServe(":60000", nil)
}
์ฅ์ ๋ถ์
- sync.Pool โ ๊ฐ๋จํ ๊ฐ์ฒด ํ ๋ฉ์ปค๋์ฆ์ ์ ๊ณตํฉ๋๋ค.
- ๋์์ฑ ์์ ์ฑ โ GC๊ฐ ๋์์ ์คํ๋์ด ์ผ์ ์ ์ง ์๊ฐ์ด ์งง์ต๋๋ค.
- ๋ฉ๋ชจ๋ฆฌ ์์ถ์ฑ โ Go์ ํ ๋น์๋ ๋น๊ต์ ํจ์จ์ ์ ๋๋ค.
๋จ์ ๋ถ์
- GC ์ผ์ ์ ์ง โ ์งง์ง๋ง, ์ฌ์ ํ ์ง์ฐ์ ๋ฏผ๊ฐํ ์ ํ๋ฆฌ์ผ์ด์ ์ ์ํฅ์ ์ค ์ ์์ต๋๋ค.
- ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋ โ Go ๋ฐํ์์ด ์ถ๊ฐ์ ์ธ ์ค๋ฒํค๋๋ฅผ ๋ฐ์์ํต๋๋ค.
- ํ ๋น ์ ๋ต โ ์์ ๊ฐ์ฒด ํ ๋น์ด ์์ ํ ์ต์ ํ๋์ง ์์ ์ ์์ต๋๋ค.
๐ Rust์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ์ฅ์
Rust์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ๋ ์์คํ ์์ค ์ฑ๋ฅ ์ต์ ํ์ ์ ์ฌ๋ ฅ์ ๋ณด์ฌ์ค๋๋ค:
use std::io::prelude::*;
use std::net::{TcpListener, TcpStream};
fn handle_client(mut stream: TcpStream) {
// ์ ๋กโ์ฝ์คํธ ์ถ์ํ โ ์ปดํ์ผ ์ ๋ฉ๋ชจ๋ฆฌ ๋ ์ด์์ ๊ฒฐ์
let mut buffer = [0u8; 1024]; // ์คํ ํ ๋น
// ์์ ๊ถ ์์คํ
์ด ๋ฉ๋ชจ๋ฆฌ ์์ ์ฑ์ ๋ณด์ฅ
let response = b"HTTP/1.1 200 OK\r\n\r\nHello";
stream.write_all(response).unwrap();
stream.flush().unwrap();
// ํจ์๊ฐ ๋๋ ๋ ๋ฉ๋ชจ๋ฆฌ๊ฐ ์๋์ผ๋ก ํด์ ๋จ
}
fn main() {
let listener = TcpListener::bind("127.0.0.1:60000").unwrap();
for stream in listener.incoming() {
let stream = stream.unwrap();
handle_client(stream);
}
}
์ฅ์ ๋ถ์
- ์ ๋กโ์ฝ์คํธ ์ถ์ํ โ ์ปดํ์ผ ํ์ ์ต์ ํ, ๋ฐํ์ ์ค๋ฒํค๋ ์์.
- GC ์ ์ง ์์ โ ๊ฐ๋น์ง ์ปฌ๋ ์ ์ผ๋ก ์ธํ ์ง์ฐ์ ์์ ํ ํํผ.
- ๋ฉ๋ชจ๋ฆฌ ์์ ์ฑ โ ์์ ๊ถ ์์คํ ์ด ์์ ์ฑ์ ๋ณด์ฅ.
- ์ ๋ฐํ ์ ์ด โ ๊ฐ๋ฐ์๊ฐ ํ ๋น ๋ฐ ํด์ ๋ฅผ ์ธ๋ฐํ๊ฒ ์กฐ์ ๊ฐ๋ฅ.
๋์ ๊ณผ์ ๋ถ์
- ํ์ต ๊ณก์ โ ์์ ๊ถ ๋ชจ๋ธ์ ์๋ฌํ๋ ๋ฐ ์๊ฐ์ด ํ์.
- ์ปดํ์ผ ์๊ฐ โ ๋ณต์กํ ์๋ช ๋ถ์์ผ๋ก ๋น๋ ์๊ฐ์ด ๋์ด๋ ์ ์์.
- ๊ฐ๋ฐ ํจ์จ์ฑ โ GC ์ธ์ด์ ๋นํด ์์ฐ์ฑ์ด ๋ฎ์ ์ ์์.
Source:
๐ฏ Production Environment Memory Optimization Practice
๐ช ์ ์์๊ฑฐ๋ ์์คํ ๋ฉ๋ชจ๋ฆฌ ์ต์ ํ
๊ฐ์ฒด ํ ์ ์ฉ
// Product information object pool
struct ProductPool {
pool: MemoryPool,
}
impl ProductPool {
fn get_product(&mut) -> Option {
self.pool.allocate(Product::new())
}
fn return_product(&mut self, handle: ProductHandle) {
self.pool.deallocate(handle.index());
}
}
๋ฉ๋ชจ๋ฆฌ ์ฌ์ ํ ๋น
// Shopping cart memory preโallocation
struct ShoppingCart {
items: Vec, // Preโallocated capacity
total: f64,
discount: f64,
}
impl ShoppingCart {
fn new() -> Self {
Self {
items: Vec::with_capacity(20), // Preโallocate 20 product slots
total: 0.0,
discount: 0.0,
}
}
}
๐ณ ๊ฒฐ์ ์์คํ ๋ฉ๋ชจ๋ฆฌ ์ต์ ํ
๊ฒฐ์ ์์คํ ์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์ ๊ฐ์ฅ ์๊ฒฉํ ์๊ตฌ์ฌํญ์ ๊ฐ์ต๋๋ค.
์ ๋กโ์นดํผ ์ค๊ณ
// Zeroโcopy payment processing
async fn process_payment(stream: &mut TcpStream) -> Result {
// Directly read into a preโallocated buffer
let buffer = &mut PAYMENT_BUFFER;
stream.read_exact(buffer).await?;
// Direct processing, no copying needed
let payment = parse_payment(buffer)?;
process_payment_internal(payment).await?;
Ok(())
}
๋ฉ๋ชจ๋ฆฌ ํ ๊ด๋ฆฌ
// Payment transaction memory pool
static PAYMENT_POOL: Lazy> = Lazy::new(|| {
MemoryPool::new(10_000) // Preโallocate 10,000 payment transactions
});
๐ฎ ๋ฏธ๋ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ํธ๋ ๋
๐ ํ๋์จ์ดโ์ง์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
๋ฏธ๋ ๋ฐํ์์ ๋ ๋ง์ ํ๋์จ์ด ๊ธฐ๋ฅ์ ํ์ฉํ ๊ฒ์ ๋๋ค.
NUMA ์ต์ ํ
// NUMAโaware memory allocation
fn numa_aware_allocate(size: usize) -> *mut u8 {
let node = get_current_numa_node();
numa_alloc_onnode(size, node)
}
์๊ตฌ ๋ฉ๋ชจ๋ฆฌ
// Persistent memory usage
struct PersistentMemory {
ptr: *mut u8,
size: usize,
}
impl PersistentMemory {
fn new(size: usize) -> Self {
let ptr = pmem_map_file(size);
Self { ptr, size }
}
}
๐ง ์ง๋ฅํ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ
๋จธ์ โ๋ฌ๋โ๊ธฐ๋ฐ ํ ๋น
// Machineโlearningโbased memory allocation
struct SmartAllocator {
model: AllocationModel,
history: Vec,
}
impl SmartAllocator {
fn predict_allocation(&self, size: usize) -> AllocationStrategy {
self.model.predict(size, &self.history)
}
}
๐ฏ ์์ฝ
- Go๋ ํธ๋ฆฌํ๊ณ ๋์์ฑ์ ์นํ์ ์ธ GC์
sync.Pool์ ํตํ ๊ฐ๋จํ ํ๋ง์ ์ ๊ณตํ์ง๋ง, ์ผ์ ์ค์ง์ ๋ฐํ์ ์ค๋ฒํค๋๊ฐ ์ฌ์ ํ ์ง์ฐ์ ๋ฏผ๊ฐํ ์ํฌ๋ก๋์ ์ํฅ์ ์ค ์ ์์ต๋๋ค. - Rust๋ GC ์ผ์ ์ค์ง๋ฅผ ์์ ํ ์์ ๊ณ , ์์ ๊ถ ๋ชจ๋ธ์ ํตํด ๊ฒฐ์ ๋ก ์ ์ฑ๋ฅ๊ณผ ์์ ์ฑ์ ์ ๊ณตํ์ง๋ง, ํ์ต ๊ณก์ ์ด ๊ฐํ๋ฅด๊ณ ์ปดํ์ผ ์๊ฐ์ด ๋ ์ค๋ ๊ฑธ๋ฆฌ๋ ๋จ์ ์ด ์์ต๋๋ค.
- ์ค์ ์์คํ (์ ์์๊ฑฐ๋, ๊ฒฐ์ ๋ฑ)์ ๊ฐ์ฒด ํ, ์ฌ์ ํ ๋น, ์ ๋ก ๋ณต์ฌ ์ค๊ณ๋ฅผ ํ์ฉํด ๋ฉ๋ชจ๋ฆฌ ์๋ ฅ์ ์ํํฉ๋๋ค.
- ์ต์ ํธ๋ ๋๋ ํ๋์จ์ด ์ง์ ๊ธฐ๋ฒ(NUMA, ์ง์ ๋ฉ๋ชจ๋ฆฌ)๊ณผ ML ๊ธฐ๋ฐ ํ ๋น์๊ฐ ์ํฌ๋ก๋ ํจํด์ ๋์ ์ผ๋ก ์ ์ํ๋ ๋ฐฉํฅ์ ๊ฐ๋ฆฌํค๊ณ ์์ต๋๋ค.
- ๊ฐ ์ธ์ด์ ํธ๋ ์ด๋์คํ๋ฅผ ์ดํดํ๊ณ ๋ชฉํ์ ๋ง๋ ์ต์ ํ๋ฅผ ์ ์ฉํจ์ผ๋ก์จ, ํ์ ํ๋ก๋์ ํ๊ฒฝ์์ ๋์ ์ฑ๋ฅ๊ณผ ๊ฒฌ๊ณ ํ ๋ฉ๋ชจ๋ฆฌ ์์ ์ฑ์ ๋์์ ๋ฌ์ฑํ ์ ์์ต๋๋ค.
Source: โฆ
๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ์ฌ์ธต ๋ถ์
์ด ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ์ ๋ํ ์ฌ์ธต ๋ถ์์ ํตํด ๋๋ ๋ค์ํ ํ๋ ์์ํฌ ๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ๊ด๋ฆฌ ์ฐจ์ด๊ฐ ์ผ๋ง๋ ํฐ์ง ๊น์ด ๊นจ๋ฌ์๋ค. Hyperlane ํ๋ ์์ํฌ์ ์ ๋กโ๊ฐ๋น์ง ์ค๊ณ๋ ์ ๋ง ์ธ์์ ์ด๋ค. ๊ฐ์ฒด ํ ๋ฐ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ ํ ๋น๊ณผ ๊ฐ์ ๊ธฐ์ ์ ์ฌ์ฉํจ์ผ๋ก์จ, ๊ทธ๊ฒ์