Compare commits

...

7 Commits

@ -4,7 +4,8 @@ INCLUDE=include
PROJECT=fcmp
OPT_FLAGS_RUST?= -C target-cpu=native
OPT_FLAGS?= -march=native -fgraphite -fopenmp -floop-parallelize-all -ftree-parallelize-loops=4
OPT_FLAGS?= -march=native -fgraphite -fopenmp -floop-parallelize-all -ftree-parallelize-loops=4 \
-floop-interchange -ftree-loop-distribution -floop-strip-mine -floop-block
FEAT_RUST?= threads
FEAT_CFLAGS?= -D_RUN_THREADED=0
@ -63,6 +64,7 @@ $(PROJECT)-debug: LDFLAGS := $(DEBUG_LDFLAGS) $(LDFLAGS)
$(PROJECT)-debug: $(OBJ)
$(CC) $^ $(CFLAGS) -o $@ $(LDFLAGS)
$(PROJECT)-rs:
cd fcmprs && OPT_FLAGS="$(OPT_FLAGS_RUST)" CARGO_FEATURES="$(FEAT_RUST)" $(MAKE)
cp -f ./fcmprs/target/release/fcmprs $@
@ -114,6 +116,8 @@ $(PROJECT)-pgo: pgo-profile
mv pgo-use $@
strip $@
clean:
cd fcmprs && make clean
rm -rf {obj,prof}

@ -20,5 +20,6 @@ panic = "abort"
[dependencies]
cfg-if = "1.0.0"
memmap = "0.7.0"
once_cell = "1.5.2"
rayon = {version = "1.5.0", optional = true}
smallvec = "1.5.0"

@ -1,4 +1,26 @@
use std::{fmt,error};
#[derive(Debug)]
/// There was a non-matching file
pub enum UnmatchError
{
Size,
Data,
Unknown,
}
impl error::Error for UnmatchError{}
impl fmt::Display for UnmatchError
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self {
Self::Size => write!(f, "size differs"),
Self::Data => write!(f, "data differs"),
_ => write!(f, "unknown error"),
}
}
}
pub trait ResultPrintExt<T>
{
@ -6,7 +28,7 @@ pub trait ResultPrintExt<T>
}
impl<T, E> ResultPrintExt<T> for Result<T,E>
where E: std::fmt::Display
where E: std::fmt::Display
{
fn discard_msg(self, msg: impl AsRef<str>) -> Option<T> {
match self {

@ -1,7 +1,12 @@
#![allow(dead_code)]
#[cfg(feature="threads")] use rayon::prelude::*;
#[allow(unused_imports)]
use std::{
path::Path,
io, fs::{self, OpenOptions,},
convert::TryInto,
};
use smallvec::SmallVec;
use cfg_if::cfg_if;
@ -18,8 +23,9 @@ mod error;
use error::ResultPrintExt as _;
mod map;
use map::MappedFile as _;
struct UnmatchError;
use error::UnmatchError;
fn main() {
let (map1, rest) = {
@ -34,15 +40,15 @@ fn main() {
std::process::exit({
if let Some(map1) = map::map(&map1).discard_msg(format!("Failed to map file {}", map1)) {
let slice = map1.as_slice();
#[cfg(feature="threads")] let map1_sz: u64 = slice.len().try_into().expect("File size could not fit into u64. This should never happen."); // For now, non-threaded mode doesn't use this.
let mut ok = true;
let chk: SmallVec<[_; 32]> = rest.filter_map(|filename| {
let path = Path::new(&filename);
if path.exists() && path.is_file() {
map::map(path).discard_msg(format!("Failed to map file {}", filename))
.or_else(|| { ok = false; None })
} else {
eprintln!("File {} does not exist or is not a normal file", filename);
ok = false;
ok=false;
None
}
}).collect();
@ -55,24 +61,34 @@ fn main() {
if #[cfg(feature="threads")] {
match chk.into_par_iter()
.map(|map| {
if let Ok(stat) = map.as_file().metadata() {
if stat.len() != map1_sz {
return Err(UnmatchError::Size);
}
if !stat.is_file() {
return Err(UnmatchError::Unknown);
}
}
if slice == map.as_slice() {
Ok(())
}else{
Err(UnmatchError)
} else {
Err(UnmatchError::Data)
}
})
.try_reduce_with(|_, _| Ok(()))
{
Some(Ok(_)) => 0,
Some(Err(_)) => 1,
Some(Err(UnmatchError::Data)) => 1,
Some(Err(UnmatchError::Size)) => 2,
None => usage(),
_ => -1,
}
} else {
match chk.into_iter()
.map(|map| {
slice == map.as_slice()
})
.try_fold((false, true), |(_, a), b| if a && b {Ok((true, true))} else {Err(UnmatchError)})
.try_fold((false, true), |(_, a), b| if a && b {Ok((true, true))} else {Err(UnmatchError::Data)})
{
Ok((true, _)) => 0,
Ok((false, _)) => usage(),

@ -1,5 +1,26 @@
use super::*;
use once_cell::sync::OnceCell;
pub trait MappedFile
{
#[inline] fn as_slice(&self) -> &[u8]
{
&self.as_map()[..]
}
fn as_map(&self) -> &memmap::Mmap;
fn as_file(&self) -> &fs::File;
}
pub trait MappedFileNew: MappedFile + Sized
{
fn try_map(file: fs::File) -> io::Result<Self>;
#[inline] fn map(file: fs::File) -> Self
{
Self::try_map(file).unwrap()
}
}
/// Represents an open and memory mapped file
#[derive(Debug)]
pub struct MemMap
@ -8,23 +29,107 @@ pub struct MemMap
file: fs::File,
}
impl MemMap
impl MappedFile for MemMap
{
/// Get the memory mapped portion as a slice
#[inline] pub fn as_slice(&self) -> &[u8]
{
fn as_slice(&self) -> &[u8] {
&self.map[..]
}
fn as_map(&self) -> &memmap::Mmap {
&self.map
}
#[inline] fn as_file(&self) -> &fs::File {
&self.file
}
}
impl MappedFileNew for MemMap
{
#[inline] fn try_map(file: fs::File) -> io::Result<Self>
{
Ok(MemMap {
map: unsafe { memmap::Mmap::map(&file)? },
file,
})
}
}
/// Attempt to map this file
pub fn map(file: impl AsRef<Path>) -> io::Result<MemMap>
pub fn map_with<M: MappedFileNew>(file: &Path) -> io::Result<M>
{
let file = OpenOptions::new()
.read(true)
.open(file)?;
Ok(MemMap {
map: unsafe { memmap::Mmap::map(&file)? },
file,
})
M::try_map(file)
}
/// Type container for memory map
pub type DefaultMapType = LazyMap;
/// Attempt to map this file to the `DefaultMapType`
pub fn map(file: impl AsRef<Path>) -> io::Result<DefaultMapType>
{
map_with(file.as_ref())
}
/// An open and maybe mapped file
#[derive(Debug)]
pub struct LazyMap
{
map: OnceCell<memmap::Mmap>,
file: fs::File,
}
impl LazyMap
{
#[inline(always)] fn get_map(&self) -> &memmap::Mmap
{
self.map.get_or_init(|| unsafe {memmap::Mmap::map(&self.file).expect("Lazy map failed")})
}
#[inline(always)] fn try_get_map(&self) -> io::Result<&memmap::Mmap>
{
self.map.get_or_try_init(|| unsafe {memmap::Mmap::map(&self.file)})
}
/// Is the memory mapped already?
#[inline] pub fn is_mapped(&self) -> bool
{
self.map.get().is_some()
}
/// Get the mapped portion if it is mapped, attempting a map if not
#[inline] pub fn try_as_slice(&self) -> io::Result<&[u8]>
{
Ok(&self.try_get_map()?[..])
}
}
impl MappedFile for LazyMap
{
/// Get the memory mapped portion as a slice
///
/// Returns blank slice if mapping fails
#[inline] fn as_slice(&self) -> &[u8]
{
self.try_get_map()
.map(|x| &x[..])
.unwrap_or(&[])
}
fn as_map(&self) -> &memmap::Mmap {
self.map.get().unwrap()
}
#[inline] fn as_file(&self) -> &fs::File {
&self.file
}
}
impl MappedFileNew for LazyMap
{
#[inline] fn try_map(file: fs::File) -> io::Result<Self>
{
Ok(LazyMap {
map: OnceCell::new(),
file,
})
}
}

@ -17,6 +17,7 @@ typedef struct mmap {
int open_and_map(const char* file, mmap_t* restrict ptr);
int unmap_and_close(mmap_t map);
int set_preload_map(mmap_t* restrict map);
#ifdef _cplusplus
}

@ -56,6 +56,12 @@ static int compare_then_close(const mmap_t * restrict map1, mmap_t map2)
return rval;
}
static void prep_map(mmap_t* restrict map)
{
if(!set_preload_map(map))
fprintf(stderr, "Error: failed to prep map %p (%d), continuing anyway\n", map->ptr, map->fd);
}
#ifdef _RUN_THREADED
struct t_task {
_Atomic int* _ALIAS othis;
@ -81,7 +87,10 @@ void proc_thread(vec_t* restrict v_tasks)
{
for(register int i=0;i<v_tasks->len;i++)
{
// Copy map into local buffer
mrest[i] = tasks[i].mthis;
// Prep this map
prep_map(&mrest[i]);
#ifdef DEBUG
frest[i] = tasks[i].fthis;
#endif
@ -137,6 +146,9 @@ int main(int argc, char** argv)
return -1;
}
// Prep map 1
prep_map(&map1);
for(register int i=0;i<nrest;i++) {
const char* f2 = frest[i];
dprintf("Attempting to map %d (%s)", i, f2);
@ -145,7 +157,11 @@ int main(int argc, char** argv)
unmap_and_close(map1);
unmap_all(mrest, i);
return -1;
}
}
#ifdef _RUN_THREADED
// Prep the new map immediately if single threaded.
else if(! (sched_should(nrest) || _RUN_THREADED)) prep_map(&mrest[i]);
#endif
}
dprintf("All map okay");
register int rval=0;
@ -157,7 +173,10 @@ int main(int argc, char** argv)
vec_t vtask_args = vec_new_with_cap(sizeof(struct t_task), nrest);
struct t_task* task_args = vtask_args.ptr;
for (int i=0;i<nrest;i++) {
// Set default return value for task (0).
rvals[i] = 0;
// Set task params
task_args[i] = (struct t_task){
.ithis = i,
.fthis = frest[i],

@ -10,8 +10,26 @@
#define FILEMODE S_IRWXU | S_IRGRP | S_IROTH
#define DEFAULT_ADVICE MADV_SEQUENTIAL
#define ADVICE DEFAULT_ADVICE | MADV_WILLNEED
#include <map.h>
static inline int _map_advise(const mmap_t* restrict map, int adv)
{
return madvise(map->ptr, map->len, adv);
}
int set_preload_map(mmap_t* restrict map)
{
if(_map_advise(map, ADVICE) != 0) {
perror("failed to advise kernel about mapped page(s)");
return 0;
}
return 1;
}
int open_and_map(const char* file, mmap_t* restrict ptr)
{
int fd;
@ -27,7 +45,7 @@ int open_and_map(const char* file, mmap_t* restrict ptr)
return 0;
}
register struct mmap map = { .fd = fd, .ptr = NULL, .len = st.st_size };
struct mmap map = { .fd = fd, .ptr = NULL, .len = st.st_size };
if ((map.ptr = mmap(NULL, map.len, PROT_READ, MAP_SHARED,fd, 0)) == MAP_FAILED) {
perror("mmap() failed");
@ -35,8 +53,15 @@ int open_and_map(const char* file, mmap_t* restrict ptr)
return 0;
}
if(_map_advise(&map, DEFAULT_ADVICE) != 0) {
perror("madvise(): failed to set default advice");
//XXX: Should this be a hard error, or should we return the map if this fails anyway?
unmap_and_close(map);
return 0;
}
*ptr = map;
return 1;
}

@ -28,7 +28,9 @@ static void* _spawn(void* _arg)
bool sched_should(size_t ntasks)
{
register size_t num = num_cpus();
static size_t num = 0;
// XXX: This is not thread-safe, but this function is only ever called by the main thread, so...
if(!num) num = num_cpus();
return (num > 1 && ntasks > 1);
}

Loading…
Cancel
Save