macOS/Apple Silicon port: NEON kernels + platform shim (PR #1 by RDouglasSharp)
This commit is contained in:
+23
-1
@@ -1,3 +1,24 @@
|
||||
UNAME_S := $(shell uname -s)
|
||||
|
||||
ifeq ($(UNAME_S),Darwin)
|
||||
# --- macOS / Apple Silicon ---
|
||||
# Apple clang non include il runtime OpenMP: se c'e' libomp di Homebrew lo usa
|
||||
# (brew install libomp), altrimenti compila single-thread (i pragma omp sono ignorati).
|
||||
# Niente -march: su arm64 NEON e' baseline (i kernel __ARM_NEON si attivano da soli).
|
||||
CC = clang
|
||||
OMPDIR := $(shell brew --prefix libomp 2>/dev/null)
|
||||
ifneq ($(OMPDIR),)
|
||||
OMPC = -Xclang -fopenmp -I$(OMPDIR)/include
|
||||
OMPL = -L$(OMPDIR)/lib -lomp
|
||||
else
|
||||
$(warning libomp non trovato: build SINGLE-THREAD. Per il multithread: brew install libomp)
|
||||
OMPC =
|
||||
OMPL =
|
||||
endif
|
||||
CFLAGS = -O3 $(OMPC) -Wall -Wextra -Wno-unused-parameter -Wno-misleading-indentation -Wno-unused-function
|
||||
LDFLAGS = -lm $(OMPL)
|
||||
else
|
||||
# --- Linux x86-64 (percorso originale, invariato) ---
|
||||
CC = gcc
|
||||
# ARCH=native -> ottimizzato per QUESTA macchina (default, piu' veloce).
|
||||
# ARCH=x86-64-v3 -> binario PORTABILE su qualsiasi x86-64 moderno con AVX2 (per distribuire).
|
||||
@@ -5,10 +26,11 @@ CC = gcc
|
||||
ARCH ?= native
|
||||
CFLAGS = -O3 -march=$(ARCH) -fopenmp -Wall -Wextra -Wno-unused-parameter -Wno-misleading-indentation -Wno-unused-function
|
||||
LDFLAGS = -lm -fopenmp
|
||||
endif
|
||||
|
||||
all: glm
|
||||
|
||||
glm: glm.c st.h json.h tok.h tok_unicode.h
|
||||
glm: glm.c st.h json.h tok.h tok_unicode.h compat.h
|
||||
$(CC) $(CFLAGS) glm.c -o glm $(LDFLAGS)
|
||||
|
||||
olmoe: olmoe.c st.h json.h
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
/* compat.h — shim di portabilita' per piattaforme non-Linux (oggi: macOS / Apple Silicon).
|
||||
* Su Linux questo header e' un NO-OP totale: nessun simbolo definito o ridefinito,
|
||||
* zero impatto sul percorso x86 esistente.
|
||||
* Regola: ogni differenza di piattaforma vive QUI; i .c restano puliti. */
|
||||
#ifndef COMPAT_H
|
||||
#define COMPAT_H
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
/* --- posix_fadvise: assente su macOS ---
|
||||
* WILLNEED -> F_RDADVISE (readahead esplicito: stessa semantica).
|
||||
* DONTNEED -> no-op: XNU non espone un drop mirato per-range; la sua unified
|
||||
* buffer cache si autoregola sotto pressione. Il motore usa DONTNEED
|
||||
* solo come consiglio, quindi ignorarlo e' corretto (e su una macchina
|
||||
* con molta RAM tenere le pagine e' proprio cio' che si vuole). */
|
||||
#ifndef POSIX_FADV_NORMAL
|
||||
#define POSIX_FADV_NORMAL 0
|
||||
#define POSIX_FADV_RANDOM 1
|
||||
#define POSIX_FADV_SEQUENTIAL 2
|
||||
#define POSIX_FADV_WILLNEED 3
|
||||
#define POSIX_FADV_DONTNEED 4
|
||||
#define POSIX_FADV_NOREUSE 5
|
||||
#endif
|
||||
static inline int compat_fadvise(int fd, off_t off, off_t len, int advice){
|
||||
if(advice==POSIX_FADV_WILLNEED){
|
||||
struct radvisory ra;
|
||||
ra.ra_offset = off;
|
||||
ra.ra_count = (int)(len>0x7FFFFFFF ? 0x7FFFFFFF : len);
|
||||
return fcntl(fd, F_RDADVISE, &ra)<0 ? -1 : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#define posix_fadvise compat_fadvise
|
||||
|
||||
/* --- O_DIRECT: assente su macOS ---
|
||||
* L'equivalente e' F_NOCACHE sul fd (bypass della unified buffer cache).
|
||||
* compat_open_direct() apre il fd "gemello" senza cache, come il twin O_DIRECT
|
||||
* di st.h. Le pread allineate a 4K del chiamante restano valide: F_NOCACHE non
|
||||
* impone vincoli di allineamento. */
|
||||
static inline int compat_open_direct(const char *path){
|
||||
int fd = open(path, O_RDONLY);
|
||||
if(fd>=0) fcntl(fd, F_NOCACHE, 1);
|
||||
return fd;
|
||||
}
|
||||
#endif /* __APPLE__ */
|
||||
|
||||
#endif /* COMPAT_H */
|
||||
@@ -32,6 +32,11 @@ static inline float hsum256(__m256 v){ /* somma orizzontale di 8 floa
|
||||
lo=_mm_add_ps(lo,hi); __m128 sh=_mm_movehl_ps(lo,lo); lo=_mm_add_ps(lo,sh);
|
||||
sh=_mm_shuffle_ps(lo,lo,1); lo=_mm_add_ss(lo,sh); return _mm_cvtss_f32(lo);
|
||||
}
|
||||
#elif defined(__ARM_NEON)
|
||||
#include <arm_neon.h> /* Apple Silicon / aarch64: kernel NEON */
|
||||
#endif
|
||||
#ifdef __APPLE__
|
||||
#include <mach/mach.h> /* host_statistics64: MemAvailable di macOS */
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
@@ -104,7 +109,13 @@ typedef struct {
|
||||
|
||||
static void usage_save(Model *m); /* cache che impara: definita accanto a stats_dump */
|
||||
static double now_s(void){ struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return t.tv_sec+t.tv_nsec*1e-9; }
|
||||
static double rss_gb(void){ struct rusage r; getrusage(RUSAGE_SELF,&r); return r.ru_maxrss/(1024.0*1024.0); }
|
||||
static double rss_gb(void){ struct rusage r; getrusage(RUSAGE_SELF,&r);
|
||||
#ifdef __APPLE__
|
||||
return r.ru_maxrss/(1024.0*1024.0*1024.0); /* macOS: ru_maxrss in BYTE */
|
||||
#else
|
||||
return r.ru_maxrss/(1024.0*1024.0); /* Linux: in KB */
|
||||
#endif
|
||||
}
|
||||
static float *falloc(int64_t n){ float *p=malloc(n*sizeof(float)); if(!p){fprintf(stderr,"OOM\n");exit(1);} return p; }
|
||||
|
||||
/* y[S,O] = x[S,I] @ W^T, W[O,I] f32 */
|
||||
@@ -123,6 +134,12 @@ static void matmul_q(float *y, const float *x, const int8_t *q, const float *sca
|
||||
for(;i+8<=I;i+=8){ __m256i wi=_mm256_cvtepi8_epi32(_mm_loadl_epi64((const __m128i*)(w+i)));
|
||||
acc=_mm256_fmadd_ps(_mm256_loadu_ps(xs+i), _mm256_cvtepi32_ps(wi), acc); }
|
||||
a=hsum256(acc);
|
||||
#elif defined(__ARM_NEON)
|
||||
float32x4_t ac0=vdupq_n_f32(0), ac1=vdupq_n_f32(0);
|
||||
for(;i+8<=I;i+=8){ int16x8_t w16=vmovl_s8(vld1_s8(w+i));
|
||||
ac0=vfmaq_f32(ac0, vld1q_f32(xs+i), vcvtq_f32_s32(vmovl_s16(vget_low_s16(w16))));
|
||||
ac1=vfmaq_f32(ac1, vld1q_f32(xs+i+4), vcvtq_f32_s32(vmovl_s16(vget_high_s16(w16)))); }
|
||||
a=vaddvq_f32(vaddq_f32(ac0,ac1));
|
||||
#endif
|
||||
for(;i<I;i++) a+=xs[i]*(float)w[i]; y[(int64_t)s*O+o]=a*sc; } }
|
||||
}
|
||||
@@ -143,6 +160,18 @@ static void matmul_i4(float *y, const float *x, const uint8_t *q4, const float *
|
||||
acc=_mm256_fmadd_ps(_mm256_loadu_ps(xs+i), w0, acc);
|
||||
acc=_mm256_fmadd_ps(_mm256_loadu_ps(xs+i+8), w1, acc); }
|
||||
a=hsum256(acc);
|
||||
#elif defined(__ARM_NEON)
|
||||
const uint8x8_t m4=vdup_n_u8(0x0F); const int8x8_t b8=vdup_n_s8(8);
|
||||
float32x4_t ac0=vdupq_n_f32(0), ac1=vdupq_n_f32(0);
|
||||
for(;i+16<=I;i+=16){ uint8x8_t by=vld1_u8(w+(i>>1)); /* 8 byte=16 nibble */
|
||||
uint8x8x2_t z=vzip_u8(vand_u8(by,m4), vshr_n_u8(by,4)); /* nibble in ordine */
|
||||
int16x8_t w0=vmovl_s8(vsub_s8(vreinterpret_s8_u8(z.val[0]),b8));
|
||||
int16x8_t w1=vmovl_s8(vsub_s8(vreinterpret_s8_u8(z.val[1]),b8));
|
||||
ac0=vfmaq_f32(ac0, vld1q_f32(xs+i), vcvtq_f32_s32(vmovl_s16(vget_low_s16(w0))));
|
||||
ac1=vfmaq_f32(ac1, vld1q_f32(xs+i+4), vcvtq_f32_s32(vmovl_s16(vget_high_s16(w0))));
|
||||
ac0=vfmaq_f32(ac0, vld1q_f32(xs+i+8), vcvtq_f32_s32(vmovl_s16(vget_low_s16(w1))));
|
||||
ac1=vfmaq_f32(ac1, vld1q_f32(xs+i+12), vcvtq_f32_s32(vmovl_s16(vget_high_s16(w1)))); }
|
||||
a=vaddvq_f32(vaddq_f32(ac0,ac1));
|
||||
#endif
|
||||
for(;i+1<I;i+=2){ uint8_t byte=w[i>>1]; int lo=(int)(byte&0xF)-8, hi=(int)(byte>>4)-8;
|
||||
a += xs[i]*(float)lo + xs[i+1]*(float)hi; }
|
||||
@@ -168,6 +197,21 @@ static void matmul_i2(float *y, const float *x, const uint8_t *q2, const float *
|
||||
acc=_mm256_fmadd_ps(_mm256_loadu_ps(xs+i), w0, acc);
|
||||
acc=_mm256_fmadd_ps(_mm256_loadu_ps(xs+i+8), w1, acc); }
|
||||
a=hsum256(acc);
|
||||
#elif defined(__ARM_NEON)
|
||||
const uint8x8_t m2v=vdup_n_u8(3); const int8x8_t b2v=vdup_n_s8(2);
|
||||
float32x4_t ac0=vdupq_n_f32(0), ac1=vdupq_n_f32(0);
|
||||
for(;i+16<=I;i+=16){ uint32_t wd; memcpy(&wd, w+(i>>2), 4); /* 4 byte=16 valori */
|
||||
uint8x8_t by=vreinterpret_u8_u32(vdup_n_u32(wd));
|
||||
uint8x8x2_t z01=vzip_u8(vand_u8(by,m2v), vand_u8(vshr_n_u8(by,2),m2v));
|
||||
uint8x8x2_t z23=vzip_u8(vand_u8(vshr_n_u8(by,4),m2v), vshr_n_u8(by,6));
|
||||
uint16x4x2_t zz=vzip_u16(vreinterpret_u16_u8(z01.val[0]), vreinterpret_u16_u8(z23.val[0]));
|
||||
int16x8_t w0=vmovl_s8(vsub_s8(vreinterpret_s8_u16(zz.val[0]),b2v)); /* 16 valori in ordine */
|
||||
int16x8_t w1=vmovl_s8(vsub_s8(vreinterpret_s8_u16(zz.val[1]),b2v));
|
||||
ac0=vfmaq_f32(ac0, vld1q_f32(xs+i), vcvtq_f32_s32(vmovl_s16(vget_low_s16(w0))));
|
||||
ac1=vfmaq_f32(ac1, vld1q_f32(xs+i+4), vcvtq_f32_s32(vmovl_s16(vget_high_s16(w0))));
|
||||
ac0=vfmaq_f32(ac0, vld1q_f32(xs+i+8), vcvtq_f32_s32(vmovl_s16(vget_low_s16(w1))));
|
||||
ac1=vfmaq_f32(ac1, vld1q_f32(xs+i+12), vcvtq_f32_s32(vmovl_s16(vget_high_s16(w1)))); }
|
||||
a=vaddvq_f32(vaddq_f32(ac0,ac1));
|
||||
#endif
|
||||
for(;i<I;i++){ uint8_t byte=w[i>>2]; int sh=(i&3)*2; a += xs[i]*(float)((int)((byte>>sh)&3)-2); }
|
||||
y[(int64_t)s*O+o]=a*sc; } }
|
||||
@@ -203,6 +247,21 @@ static inline int32_t dot_i8i8(const int8_t *w, const int8_t *x, int I){
|
||||
acc=_mm256_add_epi32(acc,_mm256_madd_epi16(p,ones));
|
||||
}
|
||||
sum=hsum256_i32(acc);
|
||||
#elif defined(__ARM_NEON)
|
||||
/* ARM: SDOT nativo se disponibile (Apple Silicon: sempre); altrimenti vmull/vpadal.
|
||||
* Stesso bound anti-overflow del trucco AVX2: coppie <= 128*127*2 = 32512 < 32767. */
|
||||
int32x4_t acc=vdupq_n_s32(0);
|
||||
for(;i+16<=I;i+=16){
|
||||
int8x16_t wv=vld1q_s8(w+i), xv=vld1q_s8(x+i);
|
||||
#if defined(__ARM_FEATURE_DOTPROD)
|
||||
acc=vdotq_s32(acc,wv,xv);
|
||||
#else
|
||||
int16x8_t p=vmull_s8(vget_low_s8(wv),vget_low_s8(xv));
|
||||
p=vmlal_s8(p,vget_high_s8(wv),vget_high_s8(xv));
|
||||
acc=vpadalq_s16(acc,p);
|
||||
#endif
|
||||
}
|
||||
sum=vaddvq_s32(acc);
|
||||
#endif
|
||||
for(;i<I;i++) sum+=(int32_t)w[i]*x[i];
|
||||
return sum;
|
||||
@@ -224,6 +283,27 @@ static inline int32_t dot_i4i8(const uint8_t *w4, const int8_t *x, int I){
|
||||
acc=_mm256_add_epi32(acc,_mm256_madd_epi16(p,ones));
|
||||
}
|
||||
sum=hsum256_i32(acc);
|
||||
#elif defined(__ARM_NEON)
|
||||
const uint8x16_t m4q=vdupq_n_u8(0x0F); const int8x16_t b8q=vdupq_n_s8(8);
|
||||
int32x4_t acc=vdupq_n_s32(0);
|
||||
for(;i+32<=I;i+=32){
|
||||
uint8x16_t by=vld1q_u8(w4+(i>>1)); /* 16 byte = 32 nibble */
|
||||
uint8x16x2_t z=vzipq_u8(vandq_u8(by,m4q), vshrq_n_u8(by,4)); /* nibble in ordine */
|
||||
int8x16_t w0=vsubq_s8(vreinterpretq_s8_u8(z.val[0]),b8q);
|
||||
int8x16_t w1=vsubq_s8(vreinterpretq_s8_u8(z.val[1]),b8q);
|
||||
int8x16_t x0=vld1q_s8(x+i), x1=vld1q_s8(x+i+16);
|
||||
#if defined(__ARM_FEATURE_DOTPROD)
|
||||
acc=vdotq_s32(acc,w0,x0); acc=vdotq_s32(acc,w1,x1);
|
||||
#else
|
||||
int16x8_t p=vmull_s8(vget_low_s8(w0),vget_low_s8(x0)); /* |w|<=8: nessun overflow */
|
||||
p=vmlal_s8(p,vget_high_s8(w0),vget_high_s8(x0));
|
||||
acc=vpadalq_s16(acc,p);
|
||||
p=vmull_s8(vget_low_s8(w1),vget_low_s8(x1));
|
||||
p=vmlal_s8(p,vget_high_s8(w1),vget_high_s8(x1));
|
||||
acc=vpadalq_s16(acc,p);
|
||||
#endif
|
||||
}
|
||||
sum=vaddvq_s32(acc);
|
||||
#endif
|
||||
for(;i+1<I;i+=2){ uint8_t b=w4[i>>1]; sum+=((int)(b&0xF)-8)*x[i]+((int)(b>>4)-8)*x[i+1]; }
|
||||
if(i<I){ uint8_t b=w4[i>>1]; sum+=((int)(b&0xF)-8)*x[i]; }
|
||||
@@ -1419,12 +1499,23 @@ static void pin_load(Model *m, const char *statspath, double gb){
|
||||
}
|
||||
|
||||
static double g_mem_avail_boot=0; /* MemAvailable all'avvio, prima di caricare il modello */
|
||||
/* RAM disponibile ADESSO (GB) da /proc/meminfo: e' il tetto vero, non il totale */
|
||||
/* RAM disponibile ADESSO (GB): e' il tetto vero, non il totale. Linux: MemAvailable
|
||||
* da /proc/meminfo. macOS: pagine free+inactive+purgeable da host_statistics64
|
||||
* (stessa semantica: recuperabili senza swap). Senza questo ramo il fallback
|
||||
* "assumo 8 GB" castrava la cache expert proprio sulle macchine con piu' RAM. */
|
||||
static double mem_available_gb(void){
|
||||
#ifdef __APPLE__
|
||||
mach_msg_type_number_t cnt=HOST_VM_INFO64_COUNT;
|
||||
vm_statistics64_data_t vm;
|
||||
if(host_statistics64(mach_host_self(),HOST_VM_INFO64,(host_info64_t)&vm,&cnt)!=KERN_SUCCESS) return 0;
|
||||
return ((double)vm.free_count+(double)vm.inactive_count+(double)vm.purgeable_count)
|
||||
* (double)sysconf(_SC_PAGESIZE) / 1e9;
|
||||
#else
|
||||
FILE *f=fopen("/proc/meminfo","r"); if(!f) return 0;
|
||||
char ln[256]; double kb=0;
|
||||
while(fgets(ln,sizeof(ln),f)) if(sscanf(ln,"MemAvailable: %lf",&kb)==1) break;
|
||||
fclose(f); return kb/1e6;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* byte disponibili per gli expert (pin + LRU) nel budget — specchio del conto di cap_for_ram */
|
||||
|
||||
+11
@@ -12,7 +12,9 @@
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#ifdef _OPENMP
|
||||
#include <omp.h>
|
||||
#endif
|
||||
static double now(){ struct timespec t; clock_gettime(CLOCK_MONOTONIC,&t); return t.tv_sec+t.tv_nsec*1e-9; }
|
||||
int main(int argc,char**argv){
|
||||
if(argc<2){fprintf(stderr,"uso: %s file [blkMB] [n] [threads] [direct 0/1]\n",argv[0]);return 1;}
|
||||
@@ -20,9 +22,18 @@ int main(int argc,char**argv){
|
||||
int n=argc>3?atoi(argv[3]):64;
|
||||
int nth=argc>4?atoi(argv[4]):8;
|
||||
int direct=argc>5?atoi(argv[5]):1;
|
||||
#ifdef O_DIRECT
|
||||
int fd=open(argv[1],O_RDONLY|(direct?O_DIRECT:0));
|
||||
if(fd<0 && direct){ fprintf(stderr,"O_DIRECT non disponibile (%s), uso buffered\n",strerror(errno));
|
||||
direct=0; fd=open(argv[1],O_RDONLY); }
|
||||
#else
|
||||
int fd=open(argv[1],O_RDONLY); /* macOS: F_NOCACHE ~ O_DIRECT */
|
||||
#ifdef __APPLE__
|
||||
if(direct && fd>=0) fcntl(fd,F_NOCACHE,1);
|
||||
#else
|
||||
if(direct){ fprintf(stderr,"O_DIRECT non disponibile, uso buffered\n"); direct=0; }
|
||||
#endif
|
||||
#endif
|
||||
if(fd<0){perror("open");return 1;}
|
||||
off_t sz=lseek(fd,0,SEEK_END);
|
||||
if(sz<blk*2){fprintf(stderr,"file troppo piccolo\n");return 1;}
|
||||
|
||||
+16
-4
@@ -7,10 +7,18 @@ cd "$(dirname "$0")"
|
||||
echo "🐦 colibrì — setup"
|
||||
|
||||
# 1) dipendenze
|
||||
command -v gcc >/dev/null || { echo "manca gcc (es: sudo apt install build-essential)"; exit 1; }
|
||||
command -v make >/dev/null || { echo "manca make"; exit 1; }
|
||||
echo " gcc: $(gcc -dumpversion) · $(nproc) core"
|
||||
echo -n " OpenMP: "; echo 'int main(){return 0;}' | gcc -fopenmp -xc - -o /tmp/_omp 2>/dev/null && echo ok || { echo "manca (libgomp)"; exit 1; }
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
command -v clang >/dev/null || { echo "manca clang (xcode-select --install)"; exit 1; }
|
||||
echo " clang: $(clang --version | head -1) · $(sysctl -n hw.ncpu) core"
|
||||
echo -n " OpenMP: "
|
||||
if brew --prefix libomp >/dev/null 2>&1; then echo "ok (libomp)"
|
||||
else echo "libomp assente -> build single-thread (consigliato: brew install libomp)"; fi
|
||||
else
|
||||
command -v gcc >/dev/null || { echo "manca gcc (es: sudo apt install build-essential)"; exit 1; }
|
||||
echo " gcc: $(gcc -dumpversion) · $(nproc) core"
|
||||
echo -n " OpenMP: "; echo 'int main(){return 0;}' | gcc -fopenmp -xc - -o /tmp/_omp 2>/dev/null && echo ok || { echo "manca (libgomp)"; exit 1; }
|
||||
fi
|
||||
|
||||
# 2) build: nativa (veloce, per QUESTA macchina). Per un binario da distribuire: make portable
|
||||
echo " compilo (ARCH=${ARCH:-native})…"
|
||||
@@ -23,7 +31,11 @@ if [ -d glm_tiny ] && [ -f ref_glm.json ]; then
|
||||
fi
|
||||
|
||||
# 4) info macchina (la velocità dipende da QUESTI due numeri, non dalla GPU)
|
||||
ram=$(awk '/MemTotal/{printf "%.0f", $2/1e6}' /proc/meminfo 2>/dev/null || echo "?")
|
||||
if [ "$(uname -s)" = "Darwin" ]; then
|
||||
ram=$(( $(sysctl -n hw.memsize) / 1000000000 ))
|
||||
else
|
||||
ram=$(awk '/MemTotal/{printf "%.0f", $2/1e6}' /proc/meminfo 2>/dev/null || echo "?")
|
||||
fi
|
||||
echo " RAM: ${ram} GB (più RAM = più expert in cache = più veloce)"
|
||||
echo
|
||||
echo "pronto. Prossimi passi:"
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <unistd.h>
|
||||
#include <dirent.h>
|
||||
#include "json.h"
|
||||
#include "compat.h"
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
@@ -78,7 +79,13 @@ static int st_open_fd(shards *S, const char *path) {
|
||||
int fd = open(path, O_RDONLY);
|
||||
if (fd < 0) { perror(path); exit(1); }
|
||||
S->paths[S->nfd] = strdup(path); S->fds[S->nfd] = fd;
|
||||
#ifdef O_DIRECT
|
||||
S->dfds[S->nfd] = open(path, O_RDONLY | O_DIRECT); /* eager: lookup poi thread-safe */
|
||||
#elif defined(__APPLE__)
|
||||
S->dfds[S->nfd] = compat_open_direct(path); /* macOS: F_NOCACHE ~ O_DIRECT */
|
||||
#else
|
||||
S->dfds[S->nfd] = -1; /* niente equivalente: solo buffered */
|
||||
#endif
|
||||
S->nfd++;
|
||||
return fd;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user