Merge branch 'histolines'

Conflicts:
	communication_techniques/src/main.c
This commit is contained in:
Thomas Preud'homme 2010-02-25 18:15:33 +01:00 committed by Thomas Preud'homme
commit 0c81f6323c
31 changed files with 1381 additions and 493 deletions

View File

@ -1 +1,2 @@
include/specific_comm.h
logs/*

View File

@ -1,60 +1,83 @@
# Directories
OBJDIR=obj
BINDIR=bin
SRCDIR=src
INCDIR=include
LIBDIR=lib
LOCALDIR=$(HOME)/local
PAPIHIGHLEVELLIBDIR=lib
PAPIHIGHLEVELINCDIR=include
OBJDIR:=obj
BINDIR:=bin
SRCDIR:=src
INCDIR:=include
LIBDIR:=lib
LOGDIR:=logs
LOCALDIR:=$(HOME)/local
# PHL stands for PAPI High Level
PHL_LIB_INSTALLDIR:=lib
PHL_INC_INSTALLDIR:=lib
PHL_LIBDIR:=lib
PHL_INCDIR:=include
PHLDIR:=../papihighlevel
PAPI_LIB_INSTALLDIR:=lib
PAPIDIR:=$(PHLDIR)/papi
PAPI_LIBDIR:=src
PAPI_PFMDIR:=$(PAPI_LIBDIR)/libpfm-3.y/lib
CALCDIR:=calculation
COMMDIR:=communication
# Compilation flags
# I know -finline-functions and -finline-functions-called-once are enabled by
# -O3 but I did this in case gcc behaviour change one day
CFLAGS=-g -O3 -finline-functions -finline-functions-called-once -Wall -Werror
LDFLAGS=-L$(LIBDIR) -L$(LOCALDIR)/$(PAPIHIGHLEVELLIBDIR) -Wl,-rpath-link,$(HOME)/local/lib -lpthread -lpapihighlevel
CFLAGS:=-c -O3 -finline-functions -finline-functions-called-once -Wall -Werror
LDFLAGS:=-L$(LIBDIR) -L$(LOCALDIR)/$(PHL_LIB_INSTALLDIR) -L$(PHLDIR)/$(PHL_LIBDIR)
LDFLAGS:=$(LDFLAGS) -Wl,-rpath-link,$(LOCALDIR)/$(PAPI_LIB_INSTALLDIR):$(PAPIDIR)/$(PAPI_LIBDIR):$(PAPIDIR)/$(PAPI_PFMDIR)
LDFLAGS:=$(LDFLAGS) -lpthread -lpapihighlevel -ldl
# Executables
CC=gcc
# Files
BINNAMES=asm_cache_comm c_cache_comm pipe_comm shared_mem_comm shared_mem_opt_comm jikes_barrier_comm fake_comm
BINS=$(patsubst %, $(BINDIR)/%, $(BINNAMES))
MAIN_OBJS=main.o common.o
COMMON_LIB_OBJS=common.o
BINNAMES:=asm_cache_comm c_cache_comm pipe_comm shared_mem_comm shared_mem_opt_comm jikes_barrier_comm fake_comm
CALCLIBSNAMES:=calc_mat calc_useless_loop
BINS:=$(patsubst %,$(BINDIR)/%,$(BINNAMES))
CALCLIBS:=$(patsubst %,$(LIBDIR)/$(CALCDIR)/lib%.so.1,$(CALCLIBSNAMES))
MAIN_OBJS:=main.o
COMMON_LIB_OBJS:=common.o
.PHONY: all tidy clean distclean symlink
.PHONY: all tidy clean distclean
.SECONDARY:
.SUFFIXES: .c .o
default: $(BINS)
ifneq (,$(findstring $(MAKECMDGOALS),$(BINS)))
BASE_TARGET=$(patsubst $(BINDIR)/%_comm,%,$(MAKECMDGOALS))
default: $(BINS) $(CALCLIBS)
# Compilation of binary
$(BINDIR)/$(BASE_TARGET)_comm: $(patsubst %,$(OBJDIR)/$(BASE_TARGET)_%,$(MAIN_OBJS)) $(LIBDIR)/lib$(BASE_TARGET).a
$(BINDIR)/%_comm: $(patsubst %,$(OBJDIR)/\%/%,$(MAIN_OBJS)) $(LIBDIR)/$(COMMDIR)/lib%.a
if [ ! -d $(BINDIR) ] ; then mkdir $(BINDIR) ; fi
$(CC) -o $@ $^ $(LDFLAGS)
# Compilation of library
$(LIBDIR)/lib$(BASE_TARGET).a: $(OBJDIR)/$(BASE_TARGET).o $(patsubst %,$(OBJDIR)/$(BASE_TARGET)_%,$(COMMON_LIB_OBJS))
# Creation of comm library
$(LIBDIR)/$(COMMDIR)/lib%.a: $(OBJDIR)/$(COMMDIR)/%.o $(patsubst %,$(OBJDIR)/$(COMMDIR)/\%/%,$(COMMON_LIB_OBJS))
if [ ! -d $(LIBDIR) ] ; then mkdir $(LIBDIR) ; fi
if [ ! -d $(LIBDIR)/$(COMMDIR) ] ; then mkdir $(LIBDIR)/$(COMMDIR) ; fi
$(AR) -rcus $@ $^
# Compile common source files
$(OBJDIR)/$(BASE_TARGET)_%.o: $(SRCDIR)/%.c symlink
if [ ! -d $(OBJDIR) ] ; then mkdir $(OBJDIR) ; fi
$(CC) $(CFLAGS) -I$(INCDIR) -I$(LOCALDIR)/$(PAPIHIGHLEVELINCDIR) -c $< -o $@
endif
# Creation of calc library
$(LIBDIR)/$(CALCDIR)/libcalc_%.so.1: $(OBJDIR)/$(CALCDIR)/calc_%.o
if [ ! -d $(LIBDIR) ] ; then mkdir $(LIBDIR) ; fi
if [ ! -d $(LIBDIR)/$(CALCDIR) ] ; then mkdir $(LIBDIR)/$(CALCDIR) ; fi
$(CC) -shared -Wl,-soname,libcalc_$*.so.1 -o $@ $<
cd $(@D) ; ln -sf libcalc_$*.so.1 libcalc_$*.so
# Compile non common source files
$(OBJDIR)/%.o: $(SRCDIR)/%.c symlink
# Compile lib specific source files
$(OBJDIR)/$(COMMDIR)/%.o: $(SRCDIR)/$(COMMDIR)/%.c $(INCDIR)/%_comm.h $(INCDIR)/commtech.h
if [ ! -d $(OBJDIR) ] ; then mkdir $(OBJDIR) ; fi
$(CC) $(CFLAGS) -I$(INCDIR) -c $< -o $@
if [ ! -d $(OBJDIR)/$(COMMDIR) ] ; then mkdir $(OBJDIR)/$(COMMDIR) ; fi
cd $(INCDIR) ; ln -sfT $*_comm.h specific_comm.h
$(CC) $(CFLAGS) -I$(INCDIR) $< -o $@
symlink:
ln -sfT $(BASE_TARGET)_comm.h $(INCDIR)/specific_comm.h
# Rule for compiling common source files using libcomm is at the end of
# this file, after the .SECONDEXPANSION target
# Compile source files not using libcomm
$(OBJDIR)/$(CALCDIR)/%.o: $(SRCDIR)/$(CALCDIR)/%.c
if [ ! -d $(OBJDIR) ] ; then mkdir $(OBJDIR) ; fi
if [ ! -d $(OBJDIR)/$(CALCDIR) ] ; then mkdir $(OBJDIR)/$(CALCDIR) ; fi
$(CC) $(CFLAGS) -I$(INCDIR) $< -o $@
#.%.d: %.c
# gcc $(CFLAGS) -MM $^ | sed -e 's/\([^:]*\):\(.*\)/\1 $@: \2 Makefile/' > $@
@ -66,6 +89,9 @@ clean:
rm -f $(INCDIR)/specific_comm.h
rm -rf $(OBJDIR)
logclean:
rm -rf $(LOGDIR)
distclean: clean
rm -rf $(BINDIR) $(LIBDIR)
@ -78,3 +104,19 @@ distclean: clean
#endif
#endif
#endif
.SECONDEXPANSION:
# Compile common source files using libcomm
$(OBJDIR)/$(COMMDIR)/%.o: $(SRCDIR)/$(COMMDIR)/$$(*F).c $(INCDIR)/$$(*D)_comm.h $(INCDIR)/commtech.h
if [ ! -d $(OBJDIR) ] ; then mkdir $(OBJDIR) ; fi
if [ ! -d $(OBJDIR)/$(COMMDIR) ] ; then mkdir $(OBJDIR)/$(COMMDIR) ; fi
if [ ! -d $(OBJDIR)/$(COMMDIR)/$(*D) ] ; then mkdir $(OBJDIR)/$(COMMDIR)/$(*D) ; fi
cd $(INCDIR) ; ln -sfT $(*D)_comm.h specific_comm.h
$(CC) $(CFLAGS) -I$(INCDIR) $< -o $@
$(OBJDIR)/%.o: $(SRCDIR)/$$(*F).c $(INCDIR)/$$(*D)_comm.h $(INCDIR)/commtech.h
if [ ! -d $(OBJDIR) ] ; then mkdir $(OBJDIR) ; fi
if [ ! -d $(OBJDIR)/$(*D) ] ; then mkdir $(OBJDIR)/$(*D) ; fi
cd $(INCDIR) ; ln -sfT $(*D)_comm.h specific_comm.h
$(CC) $(CFLAGS) -I$(INCDIR) -I$(LOCALDIR)/$(PHL_INCDIR) -I../$(PHLDIR)/$(PHL_INCDIR) $< -o $@

View File

@ -0,0 +1 @@
- move init_thread_library in papihighlevel.c in init_library (the constructor is only called once)

View File

@ -1,37 +1,36 @@
#ifndef _SPECIFIC_COMM_H_
#define _SPECIFIC_COMM_H_ 1
#include <stdint.h>
/* Non standard include */
#include <common_comm.h>
#include <commtech.h>
/* This is not an error, we need this two-macro system */
#define toString(x) doStringification(x)
#define doStringification(x) #x
struct communication_channel
__BEGIN_DECLS
struct comm_channel
{
void *buf[2 * BUF_SIZE / sizeof(void *)] __attribute__ ((aligned (CACHE_LINE_SIZE)));
int state __attribute__ ((aligned (CACHE_LINE_SIZE)));
volatile void *buf[2 * BUF_SIZE / sizeof(void *)] __attribute__ ((aligned (CACHE_LINE_SIZE)));
volatile int state __attribute__ ((aligned (CACHE_LINE_SIZE)));
int idx __attribute__ ((aligned (CACHE_LINE_SIZE)));
};
struct communication_assoc
struct thread_comm
{
struct communication_assoc *next;
struct communication_assoc *prev;
pthread_t tid;
struct communication_channel *channel;
struct comm_channel *channel;
int receiver_idx;
int unused;
};
extern struct communication_assoc assoc_root;
extern struct thread_comm *tcomms;
extern int swap_buffer;
__BEGIN_DECLS
struct communication_assoc *create_comm_assoc(void);
static inline void send(void **addr) {
int init_thread_comm(struct thread_comm *);
int end_thread_comm(void);
static inline void send(void **addr)
{
asm volatile("mov %%gs:channel@NTPOFF + 2 *" toString(BUF_SIZE) " + " toString(CACHE_LINE_SIZE) ", %%eax\n\t"
"mov %0, %%gs:channel@NTPOFF(%%eax)\n\t"
"addl $4, %%eax\n\t"

View File

@ -0,0 +1,44 @@
#ifndef _SPECIFIC_COMM_H_
#define _SPECIFIC_COMM_H_ 1
/* Non standard include */
#include <commtech.h>
/* This is not an error, we need this two-macro system */
#define toString(x) doStringification(x)
#define doStringification(x) #x
struct comm_channel
{
volatile void *buf[2 * BUF_SIZE / sizeof(void *)] __attribute__ ((aligned (CACHE_LINE_SIZE)));
volatile int state __attribute__ ((aligned (CACHE_LINE_SIZE)));
int idx __attribute__ ((aligned (CACHE_LINE_SIZE)));
};
struct thread_comm
{
struct comm_channel *channel;
int receiver_idx;
};
extern struct thread_comm *tcomms;
extern __thread struct comm_channel channel;
__BEGIN_DECLS
int init_thread_comm(struct thread_comm *);
int end_thread_comm(void);
static inline void send(void **addr)
{
channel.buf[channel.idx++] = addr;
channel.idx %= 2 * BUF_SIZE / sizeof(void *);
if (!(channel.idx % (BUF_SIZE / sizeof(void *))))
{
while (channel.state);
channel.state = 1;
}
}
__END_DECLS
#endif

View File

@ -1,7 +1,5 @@
#ifndef _COMMON_COMM_H_
#define _COMMON_COMM_H_ 1
#include <stdint.h>
#ifndef _COMMTECH_H_
#define _COMMTECH_H_ 1
#define CACHE_LINE_SIZE 128
#define BUF_SIZE CACHE_LINE_SIZE
@ -10,16 +8,15 @@
#define unlikely(x) __builtin_expect(!!(x), 0)
extern volatile int cont;
extern long nb_prod;
__BEGIN_DECLS
void add_sender(void);
void remove_sender(void);
volatile int *init_comm(void);
int init_library(void);
int end_library(void);
int init_producer_thread(void);
int end_producer_thread(void);
void reception(void (*)(void *));
extern int swap_buffer;
void wait_initialization(void);
void discover_new_producers(void);
__END_DECLS

View File

@ -1,27 +1,21 @@
#ifndef __COMM_H_
#define __COMM_H_ 1
#include <stdint.h>
/* Non standard include */
#include <common_comm.h>
#ifndef _SPECIFIC_COMM_H_
#define _SPECIFIC_COMM_H_ 1
#define FAKE_NURSERY_START 1
struct communication_assoc
struct thread_comm
{
struct communication_assoc *next;
struct communication_assoc *prev;
pthread_t tid;
int unused;
};
extern struct communication_assoc assoc_root;
extern struct thread_comm *tcomms;
extern __thread void ** volatile store_var;
__BEGIN_DECLS
struct communication_assoc *create_comm_assoc(void);
int init_thread_comm(struct thread_comm *);
int end_thread_comm(void);
static inline void send(void **addr) {
static __thread void **store_var = NULL;
store_var = addr;
}

View File

@ -1,32 +1,29 @@
#ifndef __COMM_H_
#define __COMM_H_ 1
#ifndef _SPECIFIC_COMM_H_
#define _SPECIFIC_COMM_H_ 1
#include <stdint.h>
/* Non standard include */
#include <common_comm.h>
#define FAKE_NURSERY_START 1
#define REALLY_FAKE_NURSERY_START ((uintptr_t) -1)
#define ANOTHER_FAKE_NURSERY_START ((uintptr_t) -1)
struct communication_assoc
struct thread_comm
{
struct communication_assoc *next;
struct communication_assoc *prev;
pthread_t tid;
int unused;
};
extern struct communication_assoc assoc_root;
extern struct thread_comm *tcomms;
__BEGIN_DECLS
struct communication_assoc *create_comm_assoc(void);
int init_thread_comm(struct thread_comm *);
int end_thread_comm(void);
void insert(void *);
static inline void send(void **addr) {
static inline void send(void **addr)
{
void *ptd_addr;
ptd_addr = *addr; /* NOTE: Not done in real jikes barrier */
if (!((uintptr_t) addr >= REALLY_FAKE_NURSERY_START) && ((uintptr_t) ptd_addr >= FAKE_NURSERY_START))
if (!((uintptr_t) addr >= ANOTHER_FAKE_NURSERY_START) && ((uintptr_t) ptd_addr >= FAKE_NURSERY_START))
insert(addr);
}

View File

@ -1,31 +1,23 @@
#ifndef __COMM_H_
#define __COMM_H_ 1
#ifndef _SPECIFIC_COMM_H_
#define _SPECIFIC_COMM_H_ 1
#include <stdint.h>
#include <unistd.h>
/* Non standart include */
#include <specific_comm.h>
#define READ_IDX 0
#define WRITE_IDX 1
struct communication_assoc
struct thread_comm
{
struct communication_assoc *next;
struct communication_assoc *prev;
pthread_t tid;
int *pipefd;
struct communication_channel *channel;
int receiver_idx;
};
__BEGIN_DECLS
extern __thread int pipefd[];
extern struct communication_assoc assoc_root;
extern struct thread_comm *tcomms;
struct communication_assoc *create_comm_assoc(void);
int init_thread_comm(struct thread_comm *);
int end_thread_comm(void);
static inline void send(void **addr) {
write(pipefd[WRITE_IDX], &addr, sizeof(void *));
}

View File

@ -0,0 +1,10 @@
#ifndef _PRIVATE_COMMON_H_
#define _PRIVATE_COMMON_H_ 1
__BEGIN_DECLS
void wait_initialization(void);
__END_DECLS
#endif

View File

@ -1,33 +1,29 @@
#ifndef __COMM_H_
#define __COMM_H_ 1
#include <stdint.h>
#ifndef _SPECIFIC_COMM_H_
#define _SPECIFIC_COMM_H_ 1
/* Non standard include */
#include <common_comm.h>
#include <commtech.h>
#define SHARED_SPACE_SIZE (2 * BUF_SIZE)
#define SHARED_SPACE_VOIDPTR (SHARED_SPACE_SIZE / sizeof(void *))
struct communication_assoc
struct thread_comm
{
struct communication_assoc *next;
struct communication_assoc *prev;
pthread_t tid;
void **shared_space;
volatile void **shared_space;
volatile int *cons_idx;
volatile int *prod_idx;
};
extern struct communication_assoc assoc_root;
extern struct thread_comm *tcomms;
__BEGIN_DECLS
extern __thread void **shared_space;
extern __thread volatile void **shared_space;
extern __thread volatile int prod_idx;
extern __thread volatile int cons_idx;
struct communication_assoc *create_comm_assoc(void);
int init_thread_comm(struct thread_comm *);
int end_thread_comm(void);
static inline void send(void **addr) {
while ((prod_idx + 1) % SHARED_SPACE_VOIDPTR == cons_idx);
shared_space[prod_idx] = addr;

View File

@ -1,33 +1,29 @@
#ifndef __COMM_H_
#define __COMM_H_ 1
#include <stdint.h>
#ifndef _SPECIFIC_COMM_H_
#define _SPECIFIC_COMM_H_ 1
/* Non standard include */
#include <common_comm.h>
#include <commtech.h>
#define SHARED_SPACE_SIZE (2 * BUF_SIZE)
#define SHARED_SPACE_VOIDPTR (SHARED_SPACE_SIZE / sizeof(void *))
struct communication_assoc
struct thread_comm
{
struct communication_assoc *next;
struct communication_assoc *prev;
pthread_t tid;
void **shared_space;
volatile void **shared_space;
volatile int *cons_idx;
volatile int *prod_idx;
};
extern struct communication_assoc assoc_root;
extern struct thread_comm *tcomms;
__BEGIN_DECLS
extern __thread void **shared_space;
extern __thread volatile void **shared_space;
extern __thread volatile int prod_idx;
extern __thread volatile int cons_idx;
struct communication_assoc *create_comm_assoc(void);
int init_thread_comm(struct thread_comm *);
int end_thread_comm(void);
static inline void send(void **addr) {
static __thread int local_cons_idx = 0;

View File

@ -0,0 +1,139 @@
#! /bin/bash
set -u
# Files and directories
binDir="bin"
calcDir="calculation"
PHLDir="../papihighlevel"
PHLLibDir="lib"
PAPIDir="${PHLDir}/papi"
PAPILibDir="src"
PAPIPFMDir="libpfm-3.y/lib"
# Param
binList="asm_cache_comm c_cache_comm pipe_comm shared_mem_comm shared_mem_opt_comm jikes_barrier_comm fake_comm" # Type de communication
nbProdList="1" # Nombre de cores producteurs
typeProdList="none matrice useless_loop" # Methode pour produire les valeurs
typeCacheList="L2 Memory" # Niveau de cache partage
# Const
nbIter="5000000" # Nb de lignes produites
sizeBuf="1" # En nombre de lignes de cache
# Nom generique des fichiers de log
logFileName="\$perfDirName/cache_\$typeCache-nbProd_\$nbProd-typeProd_\$typeProd-argTypeProd_\$argTypeProd-nbIter_\$nbIter-\$bin.log"
expDirName="logs"
perfDirName="$expDirName/perfCommMulti-`date +'%F-%Hh%Mm%S'`"
LD_LIBRARY_PATH="${LD_LIBRARY_PATH:-}${LD_LIBRARY_PATH:+:}${PHLDir}/${PHLLibDir}:${PAPIDir}/${PAPILibDir}:${PAPIDir}/${PAPILibDir}/${PAPIPFMDir}"
PAPILibPresent=""
PFMLibPresent=""
papiHighLevelLibPresent=""
LD_LIBRARY_PATH_LEFT="${LD_LIBRARY_PATH}:"
while [ -n "${LD_LIBRARY_PATH_LEFT}" ]
do
aLibDir="${LD_LIBRARY_PATH_LEFT%%:*}"
if [ -x ${aLibDir}/libpapi.so.3 ]
then
PAPILibPresent="1";
fi
if [ -x ${aLibDir}/libpfm.so.3 ]
then
PHLLibPresent="1";
fi
if [ -x ${aLibDir}/libpapihighlevel.so ]
then
PHLLibPresent="1";
fi
if [ -n "${PAPILibPresent}" -a -n "${PHLLibPresent}" -a -n "${PFMLibPresent}" ]
then
break
fi
LD_LIBRARY_PATH_LEFT="${LD_LIBRARY_PATH_LEFT#*:}"
done
if [ -z "${PAPILibPresent}" -o -z "${PHLLibPresent}" ]
then
echo "Libraries needed for this bench not accessible by \$LD_LIBRARY_PATH" > /dev/stderr # Is there a better way to display something on stderr ?
exit 1
fi
unset PAPILibPresent
unset PHLLibPresent
unset LD_LIBRARY_PATH_LEFT
export LD_LIBRARY_PATH
[ -d "$expDirName" ] || mkdir "$expDirName"
[ -d "$perfDirName" ] || mkdir "$perfDirName"
function_run () {
case $typeProd in
"none" ) optTypeProd="" ;;
"matrice" ) optTypeProd="-c lib/${calcDir}/libcalc_mat.so 16" ;;
"useless_loop" ) optTypeProd="-c lib/${calcDir}/libcalc_useless_loop.so ${argTypeProd}" ;;
* ) exit 1 ;;
esac
case $typeCache in
"Memory" ) optTypeCache="" ;;
"L2" ) optTypeCache="-s" ;;
* ) exit 1 ;;
esac
make $binDir/$bin
echo "On lance : \"${bin##*/} $optTypeCache $optTypeProd -p $nbProd -n $nbIter\""
beginingDate=`date +%s`
( $binDir/$bin $optTypeCache $optTypeProd -p $nbProd -n $nbIter || echo "echec experience" ) | eval tee $logFileName
endDate=`date +%s`
duration_sec=`expr \( $endDate - $beginingDate \) % 60`
duration_min=`expr \( $endDate - $beginingDate \) / 60`
duration_hrs=`expr $duration_min / 60`
duration_min=`expr $duration_min % 60`
echo "Fin de l'experience : `date +'%H:%M:%S (%d/%m/%y)'`"
echo "La duree est de : ${duration_hrs}hrs ${duration_min}min ${duration_sec}sec"
echo ""
}
echo -e "On commence les perfs\n"
globalBeginingDate=`date +%s`
for nbProd in $nbProdList ; do
for typeProd in $typeProdList; do
for typeCache in $typeCacheList ; do
for bin in $binList ; do
case $typeProd in
"useless_loop" )
case $bin in
"jikes_barrier_comm" | "asm_cache_comm" | "c_cache_comm" )
for argTypeProd in 1 `seq 5 5 50` ; do
function_run ;
done ;;
* )
argTypeProd=1 ;
function_run ;;
esac ;;
"matrice" )
argTypeProd=16 ;
function_run ;;
* )
argTypeProd=1 ;
function_run ;;
esac
done
done
done
done
globalEndDate=`date +%s`
globalDuration_sec=`expr \( $globalEndDate - $globalBeginingDate \) % 60`
globalDuration_min=`expr \( $globalEndDate - $globalBeginingDate \) / 60`
globalDuration_hrs=`expr $globalDuration_min / 60`
globalDuration_min=`expr $globalDuration_min % 60`
echo "Fin de la serie d'experience : `date +'%H:%M:%S (%d/%m/%y)'`"
echo "La duree totale est de : ${globalDuration_hrs}hrs ${globalDuration_min}min ${globalDuration_sec}sec"

View File

@ -0,0 +1,330 @@
#! /bin/bash
set -u
#barriereList="jikes_barrier asm_cache c_cache fake"
barriereList="jikes_barrier asm_cache c_cache"
communicationList="asm_cache c_cache shared_mem shared_mem_opt pipe"
comList=`ls *.log | perl -ni -e '/-([^-]+)_comm/; $a{$1}=""; END { foreach ( sort keys %a ) {print "$_ "}}'`
cacheList=`ls *log | perl -ni -e '/cache_([^-]+)-/; $a{$1}=""; END { foreach ( sort keys %a ) {print "$_ "}}'`
prodList=`ls *.log | perl -ni -e '/typeProd_([^-]+)-/; $a{$1}=""; END { foreach ( sort keys %a ) {print "$_ "}}'`
barriereCommaList=`echo $barriereList | sed "s/ /,/g"`
argTypeProdList=`eval ls *typeProd_useless_loop*{$barriereCommaList}_comm.log | perl -ni -e '/argTypeProd_([\d]+)-/; $a{$1}=""; END { foreach ( sort { $a <=> $b } keys %a ) {print "$_ "}}'`
metriqueList="cache_hits cache_miss cycles total_time"
echo "set style data histogram" > multicores.gnuplot
echo "set style histogram cluster gap 1" >> multicores.gnuplot
echo "set style fill solid border -1" >> multicores.gnuplot
echo "set boxwidth 0.9" >> multicores.gnuplot
echo "set xtic rotate by -45" >> multicores.gnuplot
echo "set bmargin 5" >> multicores.gnuplot
echo "set terminal postscript landscape color" >> multicores.gnuplot
use_histo ()
{
local prod bench
prod="$1"
bench="$2"
[ "$bench" = "communication" -o "$prod" != "useless_loop" ]
return $?
}
create_dat_header ()
{
local prod bench metrique
prod="$1"
bench="$2"
metrique="$3"
# Create file
> bench_$bench-prod_$prod-metrique_$metrique.dat
# Only one point per com and cache
if use_histo "$prod" "$bench"
then
for i in 1 2 3 # 1: total, 2: by loop, 3: by write
do
if [ $i -ne 1 ]
then
echo >> bench_$bench-prod_$prod-metrique_$metrique.dat
fi
echo -ne "Method\t\t" >> bench_$bench-prod_$prod-metrique_$metrique.dat
for cache in $cacheList
do
echo -ne "\t$cache" >> bench_$bench-prod_$prod-metrique_$metrique.dat
done
done
# Several points per com and cache (one per calc argument)
else
for i in 1 2
do
if [ $i -ne 1 ]
then
echo >> bench_$bench-prod_$prod-metrique_$metrique.dat
fi
for j in 1 2 3 # 1: total, 2: by loop, 3: by write
do
echo -ne "argTypeProd\t\t" >> bench_$bench-prod_$prod-metrique_$metrique.dat
for com in $barriereList
do
echo -ne "\t$com" >> bench_$bench-prod_$prod-metrique_$metrique.dat
done
echo >> bench_$bench-prod_$prod-metrique_$metrique.dat
done
done
fi
}
get_metric_values ()
{
local prod com cache argTypeProd logFile
prod="$1"
com="$2"
cache="$3"
argTypeProd="$4"
metriquePattern="$5"
logFile=cache_$cache-*-typeProd_$prod-argTypeProd_$argTypeProd-*-${com}_comm.log
perl -n -e "print \"\$1 \$2 \$3 \" if /$metriquePattern.* (\S+) \/ (\S+) \/ (\S+)/" $logFile
}
create_simple_dat_body ()
{
local prod bench metrique argTypeProd
prod="$1"
bench="$2"
metrique="$3"
argTypeProd="$4"
for com in `eval echo \\\$\${bench}List`
do
for i in 1 2 3 # 1: total, 2: by loop, 3: by write
do
echo -ne "\n$com\t\t" >> bench_$bench-prod_$prod-metrique_$metrique.dat
for cache in $cacheList
do
if [ $i -eq 1 ]
then
valuetmp=`get_metric_values "$prod" "$com" "$cache" "$argTypeProd" "$metriquePattern"`
eval value$cache=\"$valuetmp\"
fi
eval echo -ne "\${value$cache}" | sed -r "s/^([^ ]+ ){$((i-1))}([^ ]+).*/\t\2/" >> bench_$bench-prod_$prod-metrique_$metrique.dat
done
done
done
}
create_complex_dat_body ()
{
local prod bench metrique
prod="$1"
bench="$2"
metrique="$3"
for argTypeProd in $argTypeProdList
do
for cache in $cacheList
do
if [ $cache != ${cacheList%% *} -o $argTypeProd != ${argTypeProdList%% *} ]
then
echo "" >> bench_$bench-prod_$prod-metrique_$metrique.dat
fi
for i in 1 2 3 # 1: total, 2: by loop, 3: by write
do
echo -ne "\n$argTypeProd\t\t" >> bench_$bench-prod_$prod-metrique_$metrique.dat
for com in `eval echo \\\$\${bench}List`
do
if [ $i -eq 1 ]
then
valuetmp=`get_metric_values "$prod" "$com" "$cache" "$argTypeProd" "$metriquePattern"`
eval value$com=\"$valuetmp\"
fi
eval echo -ne "\${value$com}" | sed -r "s/^([^ ]+ ){$((i-1))}([^ ]+).*/\t\2/" >> bench_$bench-prod_$prod-metrique_$metrique.dat
done
done
done
done
}
create_dat_body ()
{
local prod bench metrique argTypeProd
prod="$1"
bench="$2"
metrique="$3"
argTypeProd="$4"
if use_histo "$prod" "$bench"
then
create_simple_dat_body "$prod" "$bench" "$metrique" "$argTypeProd" "$metriquePattern"
else
create_complex_dat_body "$prod" "$bench" "$metrique" "$argTypeProd" "$metriquePattern"
fi
}
create_gnuplot_header ()
{
local prod bench gnuplotFile
prod="$1"
bench="$2"
gnuplotFile="$3"
echo "set style fill solid border -1" > $gnuplotFile
echo "set boxwidth 0.9" >> $gnuplotFile
echo "set xtic rotate by -45" >> $gnuplotFile
echo "set bmargin 5" >> $gnuplotFile
echo "set terminal postscript landscape color" >> $gnuplotFile
if use_histo "$prod" "$bench"
then
echo "set style data histogram" >> $gnuplotFile
echo "set style histogram cluster gap 1" >> $gnuplotFile
else
echo "set style data linespoints" >> $gnuplotFile
fi
}
create_simple_gnuplot_body ()
{
local metrique ylabel yscale patternPlotFile datFile
ylabel="$1"
lineNum="$2"
datFile="$3"
patternPlotFile="$4"
echo "set ylabel \"$ylabel\"" >> $patternPlotFile.gnuplot
echo "set output '$patternPlotFile.ps'" >> $patternPlotFile.gnuplot
echo "plot '$datFile' every 3::$lineNum using 2:xtic(1) title 2 , '' every 3::$lineNum u 3 ti 3" >> $patternPlotFile.gnuplot
}
create_complex_gnuplot_body ()
{
local bench ylabel lineNum datFile patternPlotFile columnNo baseTitle
bench="$1"
ylabel="$2"
lineNum="$3"
datFile="$4"
patternPlotFile="$5"
columnNo=2
echo "set ylabel \"$ylabel\"" >> $patternPlotFile.gnuplot
echo "set output '$patternPlotFile.ps'" >> $patternPlotFile.gnuplot
for com in `eval echo \\\$\${bench}List` ; do
baseTitle=`head -1 $datFile | cut -f $((columnNo + 2))`
if [ $columnNo -ne 2 ]
then
echo -n ", '' " >> $patternPlotFile.gnuplot
else
echo -n "plot '$datFile' " >> $patternPlotFile.gnuplot
fi
echo -n "every :2:$lineNum:0:$lineNum using 1:$columnNo:xtic(1) title '$baseTitle (L2)'" >> $patternPlotFile.gnuplot
echo -n ", '' every :2:$lineNum:1:$lineNum using 1:$columnNo:xtic(1) title '$baseTitle (mem)'" >> $patternPlotFile.gnuplot
columnNo=$((columnNo + 1))
done
echo >> $patternPlotFile.gnuplot
}
create_gnuplot_body ()
{
local prod bench yscale ylabel lineNum datFile patternPlotFile
prod="$1"
bench="$2"
yscale="$3"
ylabel="$4"
lineNum="$5"
datFile="$6"
patternPlotFile="$7"
echo "set $yscale y" >> $patternPlotFile.gnuplot
if [ "$yscale" = "nologscale" ]
then
echo "set yrange [0:*]" >> $patternPlotFile.gnuplot
else
echo "set yrange [*:*]" >> $patternPlotFile.gnuplot
fi
echo "set title \"Producteur : $prod\"" >> $patternPlotFile.gnuplot
if use_histo "$prod" "$bench"
then
create_simple_gnuplot_body "$ylabel" "$lineNum" "$datFile" "$patternPlotFile"
else
create_complex_gnuplot_body "$bench" "$ylabel" "$lineNum" "$datFile" "$patternPlotFile"
fi
}
create_gnuplot_file ()
{
local prod bench metrique ylabel yscale avg lineNum
prod="$1"
bench="$2"
metrique="$3"
baseYlabel="$4"
for yscale in "nologscale" "logscale"
do
for avg in total byLoop byWrite
do
case $avg in
total)
lineNum=0
ylabel="$baseYlabel" ;;
byLoop)
lineNum=1
ylabel="$baseYlabel par boucle" ;;
byWrite)
lineNum=2
ylabel="$baseYlabel par ecriture" ;;
esac
datFile=bench_$bench-prod_$prod-metrique_$metrique.dat
patternPlotFile="bench_$bench-prod_$prod-$metrique-$avg-$yscale" # Name without extension of plot and ps files
create_gnuplot_header "$prod" "$bench" "$patternPlotFile.gnuplot"
if [ $metrique != "total_time" -o $yscale != "logscale" ]
then
create_gnuplot_body "$prod" "$bench" "$yscale" "$ylabel" "$lineNum" "$datFile" "$patternPlotFile"
fi
done
done
}
for prod in $prodList ; do
case "$prod" in
matrice) argTypeProd=16 ;;
*) argTypeProd=1 ;;
esac
for bench in "communication" "barriere" ; do
# Create dat headers
for metrique in $metriqueList ; do
case "$metrique" in
cache_hits)
metriquePattern="cache hits"
ylabel="Nb cache hit" ;;
cache_miss)
metriquePattern="cache miss"
ylabel="Nb cache miss" ;;
cycles)
metriquePattern="cycles"
ylabel="Nb cycles" ;;
total_time)
metriquePattern="total_time"
ylabel="Secondes" ;;
*)
echo "Pas de pattern pour cette métrique : $metrique"
echo "Pas de label pour cette métrique : $metrique"
exit 1 ;;
esac
create_dat_header "$prod" "$bench" "$metrique"
create_dat_body "$prod" "$bench" "$metrique" "$argTypeProd" "$metriquePattern"
create_gnuplot_file "$prod" "$bench" "$metrique" "$ylabel"
done
done
done
for gnuplotFile in *.gnuplot
do
gnuplot "$gnuplotFile"
done
for psFile in *.ps
do
ps2pdf $psFile
done

View File

@ -0,0 +1,58 @@
#include <stdlib.h>
#include <stdio.h>
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
static int *mat, *vect;
static int li;
static int n, m; /* Size of the matrice: n lines, m columns */
static int *mat_cell_ptr;
int init_calc(int size)
{
int i;
n = size;
m = size;
srand(42);
mat = (int *) malloc(n * m * sizeof(int));
if (mat == NULL)
{
fprintf(stderr, "calc_mat: Unable to allocate memory for matrice calculation\n");
return -1;
}
vect = (int *) malloc(m * sizeof(int));
if (vect == NULL)
{
free(mat);
fprintf(stderr, "calc_mat: Unable to allocate memory for matrice calculation\n");
return -1;
}
for (i = 0; i < n * m; i++)
mat[i] = rand();
li = 0;
return 0;
}
void **do_calc(void)
{
int co, p = 0;
for (co = 0; co < m; co++)
p += mat[li * m + co] * vect[li];
mat[li * m] = p;
if (unlikely(++li >= n))
li = 0;
mat_cell_ptr = &mat[li * m];
return (void **) &mat_cell_ptr;
}
int end_calc(void)
{
free(mat);
free(vect);
return 0;
}

View File

@ -0,0 +1,28 @@
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
static int nb_loop = 0, prod = 0, *prod_ptr = &prod;
volatile int fourty_two = 42;
unsigned int seedp;
int init_calc(int param_nb_loop)
{
nb_loop = param_nb_loop;
return 0;
}
void **do_calc(void)
{
int i;
for(i = 0; i < nb_loop; i++)
prod += fourty_two;
return (void **) &prod_ptr;
}
int end_calc(void)
{
return 0;
}

View File

@ -1,81 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
/* Non standard include */
#include <specific_comm.h>
struct communication_assoc assoc_root;
static pthread_mutex_t assoc_lock = PTHREAD_MUTEX_INITIALIZER;
static struct communication_assoc assoc_tmp;
static volatile int init = 0;
volatile int cont = 1;
void initialize_library(void)
{
assoc_tmp.prev = &assoc_tmp;
assoc_tmp.next = &assoc_tmp;
assoc_root.prev = &assoc_root;
assoc_root.next = &assoc_root;
}
volatile int *init_comm(void)
{
return &cont;
}
void wait_initialization(void)
{
while (!init);
}
void add_sender(void)
{
struct communication_assoc *assoc;
assoc = create_comm_assoc();
pthread_mutex_lock(&assoc_lock);
if (!init)
{
initialize_library();
init = 1;
}
assoc->next = assoc_tmp.next;
assoc_tmp.next->prev = assoc;
assoc->prev = &assoc_tmp;
assoc_tmp.next = assoc;
pthread_mutex_unlock(&assoc_lock);
}
void remove_sender()
{
printf("remove_communication_channel: Not yet implemented\n");
}
void discover_new_producers(void)
{
/* If there is some new thread for the write barrier */
if(&assoc_tmp != assoc_tmp.next)
{
/* printf("Adding a new set of producers\n"); */
pthread_mutex_lock(&assoc_lock);
/*
* list in assoc_tmp is inserted between assoc_root
* and the first elements of assoc_root list
*/
assoc_root.next->prev = assoc_tmp.prev;
assoc_tmp.prev->next = assoc_root.next;
assoc_root.next = assoc_tmp.next;
assoc_root.next->prev = &assoc_root;
/*
* assoc_tmp temporary list has been copied in
* assoc_root list. assoc_tmp is now alone and so
* double linked to itself
*/
assoc_tmp.prev = &assoc_tmp;
assoc_tmp.next = &assoc_tmp;
pthread_mutex_unlock(&assoc_lock);
}
}

View File

@ -3,21 +3,25 @@
#include <pthread.h>
/* Non standard include */
#include <common_comm.h>
#include <commtech.h>
#include <private_common.h>
#include <specific_comm.h>
__thread struct communication_channel channel;
__thread struct comm_channel channel;
struct communication_assoc *create_comm_assoc(void)
int init_thread_comm(struct thread_comm *comm)
{
struct communication_assoc *assoc;
comm->receiver_idx = 0;
comm->channel = &channel;
comm->channel->state = 0;
comm->channel->idx = 0;
return 0;
}
assoc = (struct communication_assoc *) malloc(sizeof(struct communication_assoc));
assoc->tid = pthread_self();
assoc->receiver_idx = 0;
assoc->channel = &channel;
return assoc;
int end_thread_comm(void)
{
return 0;
}
char *dstr="buffer transition\n";
@ -39,17 +43,15 @@ void reception(void (*on_receive)(void *))
{
wait_initialization();
/* printf("Activate the consumer...\n"); */
while(cont)
while (cont)
{
struct communication_assoc *cur;
int i;
discover_new_producers();
cur = assoc_root.next;
while(cur != &assoc_root)
for (i = 0; i < nb_prod; i++)
{
struct communication_channel *channel = cur->channel;
if(channel->state)
if(tcomms[i].channel->state)
{
int j, n;
/*
* cur->receiver_idx point to the last cache
* line we have read. We go to the next cache
@ -59,14 +61,19 @@ void reception(void (*on_receive)(void *))
* line we correct the pointer to point to
* the first one (this is done by the modulo)
*/
int i = cur->receiver_idx;
int n = cur->receiver_idx + (BUF_SIZE / sizeof(void *));
cur->receiver_idx = n % ((2 * BUF_SIZE) / sizeof(void *));
for(; i<n; i++)
on_receive(channel->buf[i]);
channel->state = 0;
j = tcomms[i].receiver_idx;
n = tcomms[i].receiver_idx + (BUF_SIZE / sizeof(void *));
tcomms[i].receiver_idx = n % ((2 * BUF_SIZE) / sizeof(void *));
for(; j<n; j++)
{
/*
* The behaviour of this is not documented but we know
* the values inside buf won't change during this affectation
*/
on_receive((void *) tcomms[i].channel->buf[j]);
}
tcomms[i].channel->state = 0;
}
cur = cur->next;
}
}
}

View File

@ -0,0 +1,66 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
/* Non standard include */
#include <commtech.h>
#include <private_common.h>
#include <specific_comm.h>
__thread struct comm_channel channel;
int init_thread_comm(struct thread_comm *comm)
{
comm->receiver_idx = 0;
comm->channel = &channel;
comm->channel->state = 0;
comm->channel->idx = 0;
return 0;
}
int end_thread_comm(void)
{
return 0;
}
char *dstr="buffer transition\n";
void reception(void (*on_receive)(void *))
{
wait_initialization();
/* printf("Activate the consumer...\n"); */
while (cont)
{
int i;
for (i = 0; i < nb_prod; i++)
{
if(tcomms[i].channel->state)
{
int j, n;
/*
* cur->receiver_idx point to the last cache
* line we have read. We go to the next cache
* line "+ (CACHE_LINE_SIZE >> 2)" (because
* the line is full of integer (2^2 octets)
* and then if we are after the second cache
* line we correct the pointer to point to
* the first one (this is done by the modulo)
*/
j = tcomms[i].receiver_idx;
n = tcomms[i].receiver_idx + (BUF_SIZE / sizeof(void *));
tcomms[i].receiver_idx = n % ((2 * BUF_SIZE) / sizeof(void *));
for(; j<n; j++)
{
/*
* The behaviour of this is not documented but we know
* the values inside buf won't change during this affectation
*/
on_receive((void *) tcomms[i].channel->buf[j]);
}
tcomms[i].channel->state = 0;
}
}
}
}

View File

@ -0,0 +1,85 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
/* Non standard include */
#include <specific_comm.h>
#include <commtech.h>
struct thread_comm *tcomms;
volatile int cont = 1;
static int init = 0;
static int error = 0;
static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
static pthread_cond_t init_cond = PTHREAD_COND_INITIALIZER;
int init_library(void)
{
tcomms = (struct thread_comm *) malloc(nb_prod * sizeof(struct thread_comm));
if (tcomms == NULL)
{
fprintf(stderr, "Failed to allocate %lu bytes needed by the library to work\n", nb_prod * sizeof(struct thread_comm));
return -1;
}
return 0;
}
int end_library(void)
{
free(tcomms);
return 0;
}
int get_thread_number(void)
{
static int i = 0;
static pthread_mutex_t i_lock = PTHREAD_MUTEX_INITIALIZER;
int i_local;
pthread_mutex_lock(&i_lock);
i_local = i;
i++;
pthread_mutex_unlock(&i_lock);
return i_local;
}
int init_producer_thread(void)
{
int thread_num;
thread_num = get_thread_number();
if (init_thread_comm(&tcomms[thread_num]))
{
pthread_mutex_lock(&init_lock);
error = 1;
pthread_cond_signal(&init_cond);
pthread_mutex_unlock(&init_lock);
return -1;
}
if (thread_num == nb_prod - 1)
{
pthread_mutex_lock(&init_lock);
init = 1;
pthread_cond_signal(&init_cond);
pthread_mutex_unlock(&init_lock);
}
return 0;
}
int end_producer_thread(void)
{
return end_thread_comm();
}
int wait_initialization(void)
{
pthread_mutex_lock(&init_lock);
if (!init && !error)
pthread_cond_wait(&init_cond, &init_lock);
pthread_mutex_unlock(&init_lock);
if (error)
return -1;
return 0;
}

View File

@ -0,0 +1,30 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
/* Non standard include */
#include <commtech.h>
#include <private_common.h>
#include <specific_comm.h>
__thread void ** volatile store_var = NULL;
int init_thread_comm(struct thread_comm *comm)
{
return 0;
}
int end_thread_comm(void)
{
return 0;
}
void reception(void (*on_receive)(void *))
{
wait_initialization(); /* Not needed but here for equity with others techniques */
/* printf("Activate the consumer...\n"); */
while (cont);
}

View File

@ -4,7 +4,8 @@
#include <unistd.h>
/* Non standard include */
#include <common_comm.h>
#include <commtech.h>
#include <private_common.h>
#include <specific_comm.h>
@ -32,18 +33,24 @@ static struct double_linked_list *global_head = NULL;
static int bufsenqueued = 0;
static unsigned int lock = 0;
struct communication_assoc *create_comm_assoc(void)
int init_thread_comm(struct thread_comm *comm)
{
struct communication_assoc *assoc;
void **new_buffer;
new_buffer = (void **) malloc(BUFFER_SIZE);
if (posix_memalign((void *) &new_buffer, CACHE_LINE_SIZE, BUFFER_SIZE))
{
fprintf(stderr, "Failed to allocate a new buffer for the thread\n");
return -1;
}
local_tail = new_buffer + (USABLE_BUFFER_BYTES - BYTES_IN_ADDRESS -
(USABLE_BUFFER_BYTES % (1 << LOG_BYTES_IN_ADDRESS))) + BYTES_IN_ADDRESS; // The second parenthesis is equal to 0
(USABLE_BUFFER_BYTES % (1 << LOG_BYTES_IN_ADDRESS))) + BYTES_IN_ADDRESS; /* The second parenthesis is equal to 0 */
local_tail_buffer_end = local_tail;
assoc = (struct communication_assoc *) malloc(sizeof(struct communication_assoc));
assoc->tid = pthread_self();
return assoc;
return 0;
}
int end_thread_comm(void)
{
return 0;
}
void set_next(struct double_linked_list *list, struct double_linked_list *next)
@ -85,7 +92,7 @@ static void spin_unlock(unsigned int *lock)
: "memory", "cc");
}
void enqueue(struct double_linked_list *list, int arity, int to_tail) // Insert in the shared buffer: tail here is the tail of the shared buffer
void enqueue(struct double_linked_list *list, int arity, int to_tail) /* Insert in the shared buffer: tail here is the tail of the shared buffer */
{
spin_lock(&lock);
if (to_tail)
@ -127,34 +134,39 @@ void **normalizeTail(int arity)
src++;
buffer_start++;
}
return last; // Return the buffer address of the last address (if address goes from 0 to n then it's &buf[n]
return last; /* Return the buffer address of the last address (if address goes from 0 to n then it's &buf[n] */
}
void closeAndEnqueueTail(int arity)
{
void **last;
if ((((uintptr_t) local_tail) & BUFFER_MASK) != 0) // prematurely closed, won't pass here if it comes from insert
if ((((uintptr_t) local_tail) & BUFFER_MASK) != 0) /* prematurely closed, won't pass here if it comes from insert */
last = normalizeTail(arity);
else // a full tail buffer
last = local_tail_buffer_end - BYTES_IN_ADDRESS; // last space in the buffer before the 8/16 bytes of metadata of the buffer
else /* a full tail buffer */
last = local_tail_buffer_end - BYTES_IN_ADDRESS; /* last space in the buffer before the 8/16 bytes of metadata of the buffer */
enqueue((struct double_linked_list *) ((uintptr_t) last + BYTES_IN_ADDRESS), arity, 1);
}
void checkForAsyncCollection(void)
{
if (bufsenqueued >= 262144) /* We use more than 1 Go */
{
fprintf(stderr, "We use 1.5 Go. Program terminated before kernel panic\n");
exit(1);
}
}
void tailOverflow(int arity)
{
void *new_buffer;
if (local_tail != NULL)
closeAndEnqueueTail(arity); // Add the buffer to the tail of the shared buffer
closeAndEnqueueTail(arity); /* Add the buffer to the tail of the shared buffer */
if (posix_memalign(&new_buffer, BUFFER_SIZE, BUFFER_SIZE))
fprintf(stderr, "Failed to allocate space for queue. Is metadata virtual memory exhausted?");
local_tail = (void **) ((uintptr_t) new_buffer + (USABLE_BUFFER_BYTES - BYTES_IN_ADDRESS -
(USABLE_BUFFER_BYTES % (arity << LOG_BYTES_IN_ADDRESS))) + BYTES_IN_ADDRESS); // The second parenthesis is equal to 0
(USABLE_BUFFER_BYTES % (arity << LOG_BYTES_IN_ADDRESS))) + BYTES_IN_ADDRESS); /* The second parenthesis is equal to 0 */
local_tail_buffer_end = local_tail;
checkForAsyncCollection(); // possible side-effect of alloc()
checkForAsyncCollection(); /* possible side-effect of posix_memalign() */
}
void insert(void *addr)
@ -167,9 +179,10 @@ void insert(void *addr)
void reception(void (*on_receive)(void *))
{
struct double_linked_list *list_cur;
void **buf_start, **buf_ptr;
wait_initialization();
struct double_linked_list *list_cur = NULL;
wait_initialization(); /* Not needed but here for equity with others techniques */
/* printf("Activate the consumer...\n"); */
while (cont && (global_head == NULL));
if (!cont)
@ -182,4 +195,12 @@ void reception(void (*on_receive)(void *))
on_receive(*buf_ptr);
while (cont && (list_cur->next == NULL));
} while (cont);
while (global_head != NULL)
{
void *tmp;
tmp = (void *) ((uintptr_t) global_head & ~BUFFER_MASK);
global_head = global_head->next;
free(tmp);
}
}

View File

@ -0,0 +1,64 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdint.h>
/* Non standard include */
#include <commtech.h>
#include <private_common.h>
#include <specific_comm.h>
__thread int pipefd[2];
int init_thread_comm(struct thread_comm *comm)
{
int flags;
if (pipe(pipefd))
{
fprintf(stderr, "Unable to create a pipe for pipe communication\n");
return -1;
}
flags = fcntl(pipefd[READ_IDX], F_GETFL);
fcntl(pipefd[READ_IDX], F_SETFL, flags | O_NONBLOCK);
comm->pipefd = pipefd;
return 0;
}
int end_thread_comm(void)
{
return 0;
}
void reception(void (*on_receive)(void *))
{
wait_initialization(); /* Not needed but here for equity with others techniques */
/* printf("Activate the consumer...\n"); */
while(cont)
{
int i;
for (i = 0; i < nb_prod; i++)
{
int nb_read;
for(nb_read = 0; nb_read < BUF_SIZE / sizeof(void *); nb_read++)
{
int j, n;
void *tmp_buf[BUF_SIZE / sizeof(void *)];
j = nb_read / sizeof(void *);
n = read(tcomms[i].pipefd[READ_IDX], (void *) ((uintptr_t) tmp_buf + nb_read), BUF_SIZE - nb_read);
if (n > 0)
{
nb_read += n;
for (; j + sizeof(void *) <= nb_read / sizeof(void *); j += sizeof(void *))
on_receive(tmp_buf[j]);
}
}
}
}
}

View File

@ -0,0 +1,56 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
/* Non standard include */
#include <commtech.h>
#include <private_common.h>
#include <specific_comm.h>
__thread volatile void **shared_space;
__thread volatile int cons_idx = 0;
__thread volatile int prod_idx = 0;
int init_thread_comm(struct thread_comm *comm)
{
if (posix_memalign((void *) &shared_space, CACHE_LINE_SIZE, SHARED_SPACE_SIZE))
{
fprintf(stderr, "Unable to allocate space for shared mem communication\n");
return -1;
}
comm->shared_space = shared_space;
comm->cons_idx = &cons_idx;
comm->prod_idx = &prod_idx;
return 0;
}
int end_thread_comm(void)
{
return 0;
}
void reception(void (*on_receive)(void *))
{
wait_initialization();
/* printf("Activate the consumer...\n"); */
while(cont)
{
int i;
for (i = 0; i < nb_prod; i++)
{
int cons_idx;
for(cons_idx = *tcomms[i].cons_idx; cons_idx != *tcomms[i].prod_idx; cons_idx = (cons_idx + 1) % SHARED_SPACE_VOIDPTR, *tcomms[i].cons_idx = cons_idx)
{
/*
* The behaviour of this is not documented but we know
* the values inside buf won't change during this affectation
*/
on_receive((void *) tcomms[i].shared_space[cons_idx]);
}
}
}
}

View File

@ -0,0 +1,62 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
/* Non standard include */
#include <commtech.h>
#include <private_common.h>
#include <specific_comm.h>
__thread volatile void **shared_space;
__thread volatile int cons_idx = 0;
__thread volatile int prod_idx = 0;
int init_thread_comm(struct thread_comm *comm)
{
if (posix_memalign((void *) &shared_space, CACHE_LINE_SIZE, SHARED_SPACE_SIZE))
{
fprintf(stderr, "Unable to allocate space for shared mem communication\n");
return -1;
}
comm->shared_space = shared_space;
comm->cons_idx = &cons_idx;
comm->prod_idx = &prod_idx;
return 0;
}
int end_thread_comm(void)
{
return 0;
}
void reception(void (*on_receive)(void *))
{
wait_initialization();
/* printf("Activate the consumer...\n"); */
while(cont)
{
int i;
for( i = 0; i < nb_prod; i++)
{
int cons_idx, prod_idx;
cons_idx = *tcomms[i].cons_idx;
do
{
prod_idx = *tcomms[i].prod_idx;
for(; cons_idx != prod_idx; cons_idx = (cons_idx + 1) % SHARED_SPACE_VOIDPTR)
{
/*
* The behaviour of this is not documented but we know
* the values inside buf won't change during this affectation
*/
on_receive((void *) tcomms[i].shared_space[cons_idx]);
}
} while (prod_idx != *tcomms[i].prod_idx);
*tcomms[i].cons_idx = cons_idx;
}
}
}

View File

@ -1,25 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
/* Non standard include */
#include <common_comm.h>
#include <specific_comm.h>
struct communication_assoc *create_comm_assoc(void)
{
struct communication_assoc *assoc;
assoc = (struct communication_assoc *) malloc(sizeof(struct communication_assoc));
assoc->tid = pthread_self();
return assoc;
}
void reception(void (*on_receive)(void *))
{
wait_initialization();
/* printf("Activate the consumer...\n"); */
while (cont);
}

View File

@ -9,72 +9,127 @@
#include <limits.h>
#include <sys/stat.h>
#include <string.h>
#include <dlfcn.h>
#include <sys/time.h>
/* Non standards includes */
#include <papihighlevel.h>
#include <common_comm.h>
#include <commtech.h>
#include <specific_comm.h>
static long nb_cache_lines = 0;
static long nb_prod = 0;
static long size_buf = 1;
static char *calculation_lib = NULL;
static int shared = 0;
#define toString(x) doStringification(x)
#define doStringification(x) #x
#define WORDS_PER_BUF (BUF_SIZE / sizeof(uintptr_t))
#define DIV_SEC(secs, div) ((unsigned ) (((unsigned) secs) / (unsigned long) (div)))
#define DIV_USEC(nsecs, nusecs, div) ((unsigned) (((unsigned) (nusecs) + 1000000 * \
((unsigned ) (nsecs) % (div))) / (unsigned long) (div)))
static long nb_bufs_sent = 0;
long nb_prod = 0;
static int (*init_calc)(int) = NULL;
static void **(*do_calc)(void) = NULL;
static int (*end_calc)(void) = NULL;
static int shared = 0; /* We are not shared by default */
pthread_cond_t cond_cons_has_finished = PTHREAD_COND_INITIALIZER;
pthread_mutex_t mutex_cons_has_finished = PTHREAD_MUTEX_INITIALIZER;
static int consumer_has_finished = 0;
static int producers_ended = 0;
static int init_calc_arg = 0;
void usage(char *argv[])
{
char format[] = "-n [options]";
char options[] = "Required options :\n"
"-n nb_cache_lines\tNumber of cache lines to send to another core\n"
"-p nb_producers\tNumber of producers which send data to another core\n"
"-n nb_buffer_sent\t\tNumber of buffer to send to another core\n"
"\t\t\t\tBuffer size is " toString(BUF_SIZE) " bytes\n"
"-p nb_producers\t\t\tNumber of producers which send data to another core\n"
"Facultative options :\n"
"-h\t\t\tPrint this help\n"
"-s\t\t\tShare the same L2 cache or not\n"
"-c calculation_lib\tLibrary to use for calculation\n"
"\t\t\tThis library must implement functions in calc.h\n";
"-h\t\t\t\tPrint this help\n"
"-s <level>\t\t\tShare the same L<level> cache or not\n"
"\t\t\t\tIf level is:\n"
"\t\t\t\t\t> 0, then the same L<level> must be shared\n"
"\t\t\t\t\t< 0, then different L<level> must be used\n"
"\t\t\t\t\t= 0, then no constraint is given, only main memory (RAM) is guaranteed to be shared\n"
"-c calculation_libname arg\tLibrary to use for calculation with its argument\n"
"\t\t\t\tThis library must implement functions in calc.h\n";
printf("Usage : %s %s\n", argv[0], format);
printf("Options :\n");
printf("%s\n", options);
}
int do_noinit(int unused)
{
return 0;
}
void **do_nocalc(void)
{
static int an_int, *an_int_ptr = &an_int;
return (void **) &an_int_ptr;
}
int do_noend(void)
{
return 0;
}
int analyse_options(int argc, char *argv[])
{
int opt;
opterr = 0;
while ((opt = getopt(argc, argv, ":hsc:n:p:b:")) != -1)
while ((opt = getopt(argc, argv, ":hs::c:n:p:")) != -1)
{
switch (opt)
{
case 'b' :
{
char *inval;
size_buf = strtol(optarg, &inval, 10);
if ((*optarg == '\0') || (*inval != '\0'))
{
fprintf(stderr, "Option '-b' needs an integer argument\n");
return -1;
}
if ((nb_cache_lines <= 0) || ((nb_cache_lines == LONG_MAX) && errno == ERANGE))
{
fprintf(stderr, "Number of cache lines for each buffer must be between 1 and %ld, both inclusive\n", LONG_MAX);
return -1;
}
}
break;
case 'c' :
calculation_lib = optarg;
{
struct stat file_stat;
if (stat(calculation_lib, &file_stat))
void *dl_descriptor;
if (stat(optarg, &file_stat))
{
printf("%s: %s\n", optarg, strerror(errno));
fprintf(stderr, "%s: %s\n", optarg, strerror(errno));
return -1;
}
dl_descriptor = dlopen(optarg, RTLD_LAZY | RTLD_LOCAL);
if (dl_descriptor == NULL)
{
fprintf(stderr, "dlopen error: %s\n", dlerror());
return -1;
}
init_calc = (int (*)(int)) dlsym(dl_descriptor, "init_calc");
do_calc = (void ** (*)(void)) dlsym(dl_descriptor, "do_calc");
end_calc = (int (*)(void)) dlsym(dl_descriptor, "end_calc");
if ((init_calc == NULL) || (do_calc == NULL) || (end_calc == NULL))
{
fprintf(stderr, "A symbol cannot be loaded: %s\n", dlerror());
return -1;
}
if ((optind == argc) || (*argv[optind] == '-'))
{
fprintf(stderr, "Missing argument for -c option\n");
return -1;
}
{
char *inval;
init_calc_arg = strtol(argv[optind], &inval, 10);
if ((*argv[optind] == '\0') || (*inval != '\0'))
{
fprintf(stderr, "Option '-c' needs also an integer argument\n");
return -1;
}
if ((init_calc_arg <= 0) || ((init_calc_arg == LONG_MAX) && errno == ERANGE))
{
fprintf(stderr, "Number of useless loop to be done between 2 send must be"
" between 1 and %ld, both inclusive\n", LONG_MAX);
return -1;
}
}
optind++;
}
break;
case 'h' :
@ -83,13 +138,13 @@ int analyse_options(int argc, char *argv[])
case 'n' :
{
char *inval;
nb_cache_lines = strtol(optarg, &inval, 10);
nb_bufs_sent = strtol(optarg, &inval, 10);
if ((*optarg == '\0') || (*inval != '\0'))
{
fprintf(stderr, "Option '-n' needs an integer argument\n");
return -1;
}
if ((nb_cache_lines <= 0) || ((nb_cache_lines == LONG_MAX) && errno == ERANGE))
if ((nb_bufs_sent <= 0) || ((nb_bufs_sent == LONG_MAX) && errno == ERANGE))
{
fprintf(stderr, "Number of cache lines to be sent must be between 1 and %ld, both inclusive\n", LONG_MAX);
return -1;
@ -105,7 +160,7 @@ int analyse_options(int argc, char *argv[])
fprintf(stderr, "Option '-p' needs an integer argument\n");
return -1;
}
if ((nb_cache_lines <= 0) || ((nb_cache_lines == LONG_MAX) && errno == ERANGE))
if ((nb_prod <= 0) || ((nb_prod == LONG_MAX) && errno == ERANGE))
{
fprintf(stderr, "Number of producers must be between 1 and %ld, both inclusive\n", LONG_MAX);
return -1;
@ -113,8 +168,28 @@ int analyse_options(int argc, char *argv[])
}
break;
case 's' :
shared = 1;
/* TODO: shared L2 cache */
if ((optind != argc) && (*argv[optind] != '-'))
{
int share_level;
char *inval;
share_level = strtol(argv[optind], &inval, 10);
if ((*argv[optind] == '\0') || (*inval != '\0'))
{
fprintf(stderr, "Option '-p' needs an integer argument\n");
return -1;
}
if ((share_level == LONG_MIN) || ((share_level == LONG_MAX) && errno == ERANGE))
{
fprintf(stderr, "Shared memory level must be between %ld and %ld, both inclusive\n", LONG_MIN, LONG_MAX);
return -1;
}
/* TODO: Real management of shared memory level */
if (share_level <= 0) /* -x: We wan't level x not to be shared; 0 do as we want, only memory is guaranteed to be shared */
shared = 0;
else
shared = 1;
optind++;
}
break;
case '?' :
fprintf(stderr, "Option inconnue\n");
@ -127,7 +202,7 @@ int analyse_options(int argc, char *argv[])
return -1;
}
}
if (!nb_cache_lines)
if (!nb_bufs_sent)
{
fprintf(stderr, "You must give the number of cache lines to be sent\n");
return -1;
@ -142,16 +217,36 @@ int analyse_options(int argc, char *argv[])
fprintf(stderr, "Too many producers to fit with the consumer in processors which share a same cache\n");
return -1;
}
if (do_calc == NULL)
{
init_calc = do_noinit;
do_calc = do_nocalc;
end_calc = do_noend;
}
return 0;
}
void *producer(void *cont_ptr_void)
void wait_consumer(void)
{
pthread_mutex_lock(&mutex_cons_has_finished);
if (++producers_ended == nb_prod)
cont = 0;
if (!consumer_has_finished)
pthread_cond_wait(&cond_cons_has_finished, &mutex_cons_has_finished);
pthread_mutex_unlock(&mutex_cons_has_finished);
}
void *producer(void *unused)
{
int i, j;
void *k;
volatile int *cont;
struct timeval tv1, tv2, tv_result;
cont = *((volatile int **) cont_ptr_void);
if (init_producer_thread())
{
fprintf(stderr, "Initialization of thread has failed\n");
wait_consumer();
return &nb_prod; /* &nb_prod can't be NULL, whatever NULL is bound to */
}
if (shared)
{
pthread_t tid;
@ -163,7 +258,8 @@ void *producer(void *cont_ptr_void)
if (pthread_setaffinity_np(tid, sizeof(cpu_set_t), &cpuset))
{
perror("pthread_setaffinity_np");
return NULL;
wait_consumer();
return &nb_prod; /* &nb_prod can't be NULL, whatever NULL is bound to */
}
}
else
@ -177,33 +273,58 @@ void *producer(void *cont_ptr_void)
if (pthread_setaffinity_np(tid, sizeof(cpu_set_t), &cpuset))
{
perror("pthread_setaffinity_np");
return NULL;
wait_consumer();
return &nb_prod; /* &nb_prod can't be NULL, whatever NULL is bound to */
}
}
printf("Registering: %p !\n", (void*) pthread_self());
add_sender();
k = cont_ptr_void;
if (init_calc(init_calc_arg))
{
fprintf(stderr, "Initialization of calculation has failed\n");
wait_consumer();
return &nb_prod; /* nb_prod can't be NULL, whatever NULL is bound to */
}
gettimeofday(&tv1, NULL);
if (initialize_papi() != -1)
{
for(i = 0; i < nb_cache_lines; i++) {
//printf("[%p] Send a new CACHE_LINE\n", (void *) pthread_self());
for(j = 0; j < (CACHE_LINE_SIZE / sizeof(uintptr_t)); j++)
send(&k);
for(i = 0; i < nb_bufs_sent; i++) {
//printf("[%p] Send %d new CACHE_LINE\n", (void *) pthread_self(), BUF_SIZE / CACHE_LINE_SIZE);
for(j = 0; j < WORDS_PER_BUF; j++)
send(do_calc());
}
print_results();
print_results(WORDS_PER_BUF, nb_bufs_sent);
}
gettimeofday(&tv2, NULL);
tv_result.tv_sec = tv2.tv_sec - tv1.tv_sec;
if (tv2.tv_usec < tv1.tv_usec)
{
tv_result.tv_usec = tv1.tv_usec - tv2.tv_usec;
tv_result.tv_sec--;
}
else
tv_result.tv_usec = tv2.tv_usec - tv1.tv_usec;
printf("total_time: %u.%06u / %u.%06u / %u.%06u\n", (unsigned) tv_result.tv_sec,
(unsigned) tv_result.tv_usec,
DIV_SEC(tv_result.tv_sec, nb_bufs_sent),
DIV_USEC(tv_result.tv_sec, tv_result.tv_usec, nb_bufs_sent),
DIV_SEC(tv_result.tv_sec, nb_bufs_sent * WORDS_PER_BUF),
DIV_USEC(tv_result.tv_sec, tv_result.tv_usec, nb_bufs_sent * WORDS_PER_BUF));
if (end_calc())
{
fprintf(stderr, "uninitialization of calculation has failed\n");
wait_consumer();
return &nb_prod; /* &nb_prod can't be NULL, whatever NULL is bound to */
}
printf("[%p] Producer finished !\n", (void*) pthread_self());
remove_sender();
/*
* When a producer end its thread-local storage vanished. Thus,
* producers must finish only after consumer has stopped using them
*/
pthread_mutex_lock(&mutex_cons_has_finished);
if (++producers_ended == nb_prod)
*cont = 0;
if (!consumer_has_finished)
pthread_cond_wait(&cond_cons_has_finished, &mutex_cons_has_finished);
pthread_mutex_unlock(&mutex_cons_has_finished);
wait_consumer();
if (end_producer_thread())
{
fprintf(stderr, "Uninitialization of thread has failed\n");
return &nb_prod; /* &nb_prod can't be NULL, whatever NULL is bound to */
}
return NULL;
}
@ -238,21 +359,34 @@ void *receptor(void *a)
int main(int argc, char *argv[])
{
int i;
volatile int *cont;
int i, global_return_value = EXIT_SUCCESS;
void *return_value;
pthread_t *tid;
if (analyse_options(argc, argv))
return EXIT_FAILURE;
if (init_library())
return EXIT_FAILURE;
tid = (pthread_t *) malloc((nb_prod + 1) * sizeof(pthread_t));
cont = init_comm();
if (tid == NULL)
{
fprintf(stderr, "Failed to allocate %lu bytes needed for thread creation\n", (nb_prod + 1) * sizeof(pthread_t));
return EXIT_FAILURE;
}
for(i = 0; i < nb_prod; i++)
pthread_create(&tid[i], NULL, producer, &cont);
pthread_create(&tid[i], NULL, producer, NULL);
pthread_create(&tid[i], NULL, receptor, NULL);
for(i = 0; i < nb_prod; i++)
{
pthread_join(tid[i], &return_value);
if (return_value != NULL)
global_return_value = EXIT_FAILURE;
}
pthread_join(tid[i], &return_value);
if (return_value != NULL)
global_return_value = EXIT_FAILURE;
free(tid);
return EXIT_SUCCESS;
if (end_library())
return EXIT_FAILURE;
return global_return_value;
}

View File

@ -1,47 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
/* Non standard include */
#include <common_comm.h>
#include <specific_comm.h>
__thread int pipefd[2];
struct communication_assoc *create_comm_assoc(void)
{
struct communication_assoc *assoc;
pipe(pipefd);
assoc = (struct communication_assoc *) malloc(sizeof(struct communication_assoc));
assoc->tid = pthread_self();
assoc->pipefd = pipefd;
return assoc;
}
void reception(void (*on_receive)(void *))
{
wait_initialization();
/* printf("Activate the consumer...\n"); */
while(cont)
{
struct communication_assoc *cur;
discover_new_producers();
cur = assoc_root.next;
while(cur != &assoc_root)
{
int i;
for(i = 0; i < BUF_SIZE / sizeof(void *); i++)
{
void *tmp;
read(cur->pipefd[READ_IDX], &tmp, sizeof(void *));
on_receive(tmp);
}
cur = cur->next;
}
}
}

View File

@ -1,51 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
/* Non standard include */
#include <common_comm.h>
#include <specific_comm.h>
__thread void **shared_space;
__thread volatile int cons_idx = 0;
__thread volatile int prod_idx = 0;
struct communication_assoc *create_comm_assoc(void)
{
struct communication_assoc *assoc;
shared_space = (void **) malloc(SHARED_SPACE_SIZE);
assoc = (struct communication_assoc *) malloc(sizeof(struct communication_assoc));
assoc->tid = pthread_self();
assoc->shared_space = shared_space;
assoc->cons_idx = &cons_idx;
assoc->prod_idx = &prod_idx;
return assoc;
}
void reception(void (*on_receive)(void *))
{
wait_initialization();
/* printf("Activate the consumer...\n"); */
while(cont)
{
struct communication_assoc *cur;
discover_new_producers();
cur = assoc_root.next;
while(cur != &assoc_root)
{
int cons_idx;
for(cons_idx = *cur->cons_idx; cons_idx != *cur->prod_idx; cons_idx = (cons_idx + 1) % SHARED_SPACE_VOIDPTR, *cur->cons_idx = cons_idx)
{
void *tmp;
tmp = cur->shared_space[cons_idx];
on_receive(tmp);
}
cur = cur->next;
}
}
}

View File

@ -1,57 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
/* Non standard include */
#include <common_comm.h>
#include <specific_comm.h>
__thread void **shared_space;
__thread volatile int cons_idx = 0;
__thread volatile int prod_idx = 0;
struct communication_assoc *create_comm_assoc(void)
{
struct communication_assoc *assoc;
shared_space = (void **) malloc(SHARED_SPACE_SIZE);
assoc = (struct communication_assoc *) malloc(sizeof(struct communication_assoc));
assoc->tid = pthread_self();
assoc->shared_space = shared_space;
assoc->cons_idx = &cons_idx;
assoc->prod_idx = &prod_idx;
return assoc;
}
void reception(void (*on_receive)(void *))
{
wait_initialization();
/* printf("Activate the consumer...\n"); */
while(cont)
{
struct communication_assoc *cur;
discover_new_producers();
cur = assoc_root.next;
while(cur != &assoc_root)
{
int cons_idx, prod_idx;
cons_idx = *cur->cons_idx;
do
{
prod_idx = *cur->prod_idx;
for(; cons_idx != prod_idx; cons_idx = (cons_idx + 1) % SHARED_SPACE_VOIDPTR)
{
void *tmp;
tmp = cur->shared_space[cons_idx];
on_receive(tmp);
}
} while (prod_idx != *cur->prod_idx);
*cur->cons_idx = cons_idx;
cur = cur->next;
}
}
}

@ -1 +1 @@
Subproject commit 6d5117308f78e2ca984691d4440b6a1aa45857b9
Subproject commit 16449c571f7363d0a6dc8293df8f0aaec40b492a