From 25aecb96e098664e3ca472afdc8a93657ec52317 Mon Sep 17 00:00:00 2001 From: Karthik Gururaj Date: Sat, 18 Jan 2014 11:07:23 -0800 Subject: [PATCH 01/10] Added support for dynamic selection between AVX and un-vectorized C++, still to include SSE code from Mohammad. Debug flags turned on in this commit. --- .gitignore | 13 ++ PairHMM_JNI/Makefile | 45 +++-- PairHMM_JNI/avx_function_instantiations.cc | 12 ++ PairHMM_JNI/avx_function_prototypes.h | 19 ++ PairHMM_JNI/baseline.cc | 85 +++++++++ PairHMM_JNI/headers.h | 24 +++ PairHMM_JNI/libJNILoglessPairHMM.so | Bin 77757 -> 96945 bytes ...e_sting_utils_pairhmm_JNILoglessPairHMM.cc | 125 +++++++++---- PairHMM_JNI/pairhmm-1-base.cc | 177 +++++------------- PairHMM_JNI/pairhmm-template-kernel.cc | 10 +- PairHMM_JNI/pairhmm-template-main.cc | 5 +- PairHMM_JNI/run.sh | 9 +- PairHMM_JNI/template.h | 28 +-- PairHMM_JNI/{convert_char.cc => utils.cc} | 39 ++++ PairHMM_JNI/utils.h | 13 ++ .../utils/pairhmm/JNILoglessPairHMM.java | 7 +- 16 files changed, 399 insertions(+), 212 deletions(-) create mode 100644 PairHMM_JNI/avx_function_instantiations.cc create mode 100644 PairHMM_JNI/avx_function_prototypes.h create mode 100644 PairHMM_JNI/baseline.cc create mode 100644 PairHMM_JNI/headers.h rename PairHMM_JNI/{convert_char.cc => utils.cc} (84%) create mode 100644 PairHMM_JNI/utils.h diff --git a/.gitignore b/.gitignore index 65f111587..ede62ef81 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,16 @@ dump/ lib/ out/ /atlassian-ide-plugin.xml +kg_tmp/ +maven-ant-tasks-2.1.3.jar +null-sequenceGraph.25.0.0.raw_readthreading_graph.dot +null-sequenceGraph.25.0.1.cleaned_readthreading_graph.dot +null-sequenceGraph.25.0.1.initial_seqgraph.dot +null-sequenceGraph.3.0.0.raw_readthreading_graph.dot +null-sequenceGraph.3.0.1.cleaned_readthreading_graph.dot +null-sequenceGraph.3.0.1.initial_seqgraph.dot +org/ +package-list +resources/ +velocity.log + diff --git a/PairHMM_JNI/Makefile b/PairHMM_JNI/Makefile index 70bf96dd3..62dc3b0f6 100644 --- a/PairHMM_JNI/Makefile +++ b/PairHMM_JNI/Makefile @@ -7,40 +7,59 @@ OMPCFLAGS=-fopenmp JAVA_ROOT=/opt/jdk1.7.0_25/ JNI_COMPILATION_FLAGS=-D_REENTRANT -fPIC -I${JAVA_ROOT}/include -I${JAVA_ROOT}/include/linux -CFLAGS=-O3 -W -Wall -pedantic $(OMPCFLAGS) -Wno-unknown-pragmas -xAVX - -CXXFLAGS=$(CFLAGS) +COMMON_COMPILATION_FLAGS=$(JNI_COMPILATION_FLAGS) -O3 -W -Wall -pedantic $(OMPCFLAGS) -Wno-unknown-pragmas CC=icc CXX=icc LDFLAGS=-lm $(OMPLDFLAGS) -#BIN:=pairhmm-1-base #pairhmm-2-omp pairhmm-3-hybrid-float-double pairhmm-4-hybrid-diagonal pairhmm-5-hybrid-diagonal-homogeneus pairhmm-6-onlythreediags pairhmm-7-presse pairhmm-8-sse #pairhmm-dev -BIN:=libJNILoglessPairHMM.so pairhmm-template-main checker +BIN=libJNILoglessPairHMM.so pairhmm-template-main checker +#BIN=checker -#SOURCES=pairhmm-1-base.cc input.cc -LIBSOURCES=org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc convert_char.cc -SOURCES=$(LIBSOURCES) pairhmm-template-main.cc pairhmm-1-base.cc -LIBOBJECTS=$(LIBSOURCES:.cc=.o) DEPDIR=.deps DF=$(DEPDIR)/$(*).d +#Common across libJNI and sandbox +COMMON_SOURCES=utils.cc avx_function_instantiations.cc baseline.cc +#Part of libJNI +LIBSOURCES=org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc $(COMMON_SOURCES) +SOURCES=$(LIBSOURCES) pairhmm-template-main.cc pairhmm-1-base.cc +LIBOBJECTS=$(LIBSOURCES:.cc=.o) +COMMON_OBJECTS=$(COMMON_SOURCES:.cc=.o) + + +#No vectorization for these files +NO_VECTOR_SOURCES=org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc pairhmm-template-main.cc pairhmm-1-base.cc utils.cc baseline.cc +#Use -xAVX for these files +AVX_SOURCES=avx_function_instantiations.cc +#Use -xSSE4.2 for these files +SSE_SOURCES= + +NO_VECTOR_OBJECTS=$(NO_VECTOR_SOURCES:.cc=.o) +AVX_OBJECTS=$(AVX_SOURCES:.cc=.o) +SSE_OBJECTS=$(SSE_SOURCES:.cc=.o) +$(NO_VECTOR_OBJECTS): CXXFLAGS=$(COMMON_COMPILATION_FLAGS) +$(AVX_OBJECTS): CXXFLAGS=$(COMMON_COMPILATION_FLAGS) -xAVX +$(SSE_OBJECTS): CXXFLAGS=$(COMMON_COMPILATION_FLAGS) -xSSE4.2 +OBJECTS=$(NO_VECTOR_OBJECTS) $(AVX_OBJECTS) $(SSE_OBJECTS) + all: $(BIN) -include $(addprefix $(DEPDIR)/,$(SOURCES:.cc=.d)) -checker: pairhmm-1-base.o convert_char.o +checker: pairhmm-1-base.o $(COMMON_OBJECTS) $(CXX) $(OMPCFLAGS) -o $@ $^ $(LDFLAGS) -pairhmm-template-main: pairhmm-template-main.o convert_char.o +pairhmm-template-main: pairhmm-template-main.o $(COMMON_OBJECTS) $(CXX) $(OMPCFLAGS) -o $@ $^ $(LDFLAGS) libJNILoglessPairHMM.so: $(LIBOBJECTS) $(CXX) $(OMPCFLAGS) -shared -o $@ $(LIBOBJECTS) -%.o: %.cc + +$(OBJECTS): %.o: %.cc @mkdir -p $(DEPDIR) - $(COMPILE.cpp) -MMD -MF $(DF) $(JNI_COMPILATION_FLAGS) $(CXXFLAGS) $(OUTPUT_OPTION) $< + $(CXX) -c -MMD -MF $(DF) $(CXXFLAGS) $(OUTPUT_OPTION) $< clean: diff --git a/PairHMM_JNI/avx_function_instantiations.cc b/PairHMM_JNI/avx_function_instantiations.cc new file mode 100644 index 000000000..c29370561 --- /dev/null +++ b/PairHMM_JNI/avx_function_instantiations.cc @@ -0,0 +1,12 @@ +#include "template.h" + +#include "define-float.h" +#include "shift_template.c" +#include "pairhmm-template-kernel.cc" + +#include "define-double.h" +#include "shift_template.c" +#include "pairhmm-template-kernel.cc" + +template double compute_full_prob_avxd(testcase* tc, double* nextlog); +template float compute_full_prob_avxs(testcase* tc, float* nextlog); diff --git a/PairHMM_JNI/avx_function_prototypes.h b/PairHMM_JNI/avx_function_prototypes.h new file mode 100644 index 000000000..1836a1c37 --- /dev/null +++ b/PairHMM_JNI/avx_function_prototypes.h @@ -0,0 +1,19 @@ +void GEN_INTRINSIC(_vector_shift, PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn, MAIN_TYPE &shiftOut); +void GEN_INTRINSIC(_vector_shift_last, PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn); +void GEN_INTRINSIC(precompute_masks_, PRECISION)(const testcase& tc, int COLS, int numMaskVecs, MASK_TYPE (*maskArr)[NUM_DISTINCT_CHARS]); +void GEN_INTRINSIC(init_masks_for_row_, PRECISION)(const testcase& tc, char* rsArr, MASK_TYPE* lastMaskShiftOut, int beginRowIndex, int numRowsToProcess); +void GEN_INTRINSIC(update_masks_for_cols_, PRECISION)(int maskIndex, MASK_VEC& currMaskVecLow, MASK_VEC& currMaskVecHigh, MASK_TYPE (*maskArr) [NUM_DISTINCT_CHARS], char* rsArr, MASK_TYPE* lastMaskShiftOut, MASK_TYPE maskBitCnt); +void GEN_INTRINSIC(computeDistVec, PRECISION) (MASK_VEC& currMaskVecLow, MASK_VEC& currMaskVecHigh, _256_TYPE& distm, _256_TYPE& _1_distm, _256_TYPE& distmChosen); +template void GEN_INTRINSIC(initializeVectors, PRECISION)(int ROWS, int COLS, NUMBER* shiftOutM, NUMBER *shiftOutX, NUMBER *shiftOutY, Context ctx, testcase *tc, _256_TYPE *p_MM, _256_TYPE *p_GAPM, _256_TYPE *p_MX, _256_TYPE *p_XX, _256_TYPE *p_MY, _256_TYPE *p_YY, _256_TYPE *distm1D); +template void GEN_INTRINSIC(stripINITIALIZATION, PRECISION)( + int stripIdx, Context ctx, testcase *tc, _256_TYPE &pGAPM, _256_TYPE &pMM, _256_TYPE &pMX, _256_TYPE &pXX, _256_TYPE &pMY, _256_TYPE &pYY, + _256_TYPE &rs, UNION_TYPE &rsN, _256_TYPE &distm, _256_TYPE &_1_distm, _256_TYPE *distm1D, _256_TYPE N_packed256, _256_TYPE *p_MM , _256_TYPE *p_GAPM , + _256_TYPE *p_MX, _256_TYPE *p_XX , _256_TYPE *p_MY, _256_TYPE *p_YY, UNION_TYPE &M_t_2, UNION_TYPE &X_t_2, UNION_TYPE &M_t_1, UNION_TYPE &X_t_1, + UNION_TYPE &Y_t_2, UNION_TYPE &Y_t_1, UNION_TYPE &M_t_1_y, NUMBER* shiftOutX, NUMBER* shiftOutM); +_256_TYPE GEN_INTRINSIC(computeDISTM, PRECISION)(int d, int COLS, testcase * tc, HAP_TYPE &hap, _256_TYPE rs, UNION_TYPE rsN, _256_TYPE N_packed256, + _256_TYPE distm, _256_TYPE _1_distm); +void GEN_INTRINSIC(computeMXY, PRECISION)(UNION_TYPE &M_t, UNION_TYPE &X_t, UNION_TYPE &Y_t, UNION_TYPE &M_t_y, + UNION_TYPE M_t_2, UNION_TYPE X_t_2, UNION_TYPE Y_t_2, UNION_TYPE M_t_1, UNION_TYPE X_t_1, UNION_TYPE M_t_1_y, UNION_TYPE Y_t_1, + _256_TYPE pMM, _256_TYPE pGAPM, _256_TYPE pMX, _256_TYPE pXX, _256_TYPE pMY, _256_TYPE pYY, _256_TYPE distmSel); +template NUMBER GEN_INTRINSIC(compute_full_prob_avx, PRECISION) (testcase *tc, NUMBER *before_last_log = NULL); + diff --git a/PairHMM_JNI/baseline.cc b/PairHMM_JNI/baseline.cc new file mode 100644 index 000000000..b953c4436 --- /dev/null +++ b/PairHMM_JNI/baseline.cc @@ -0,0 +1,85 @@ +#include "headers.h" +#include "template.h" + +template +NUMBER compute_full_prob(testcase *tc, NUMBER *before_last_log = NULL) +{ + int r, c; + int ROWS = tc->rslen + 1; + int COLS = tc->haplen + 1; + + Context ctx; + + NUMBER M[MROWS][MCOLS]; + NUMBER X[MROWS][MCOLS]; + NUMBER Y[MROWS][MCOLS]; + NUMBER p[MROWS][6]; + + p[0][MM] = ctx._(0.0); + p[0][GapM] = ctx._(0.0); + p[0][MX] = ctx._(0.0); + p[0][XX] = ctx._(0.0); + p[0][MY] = ctx._(0.0); + p[0][YY] = ctx._(0.0); + for (r = 1; r < ROWS; r++) + { + int _i = tc->i[r-1] & 127; + int _d = tc->d[r-1] & 127; + int _c = tc->c[r-1] & 127; + p[r][MM] = ctx._(1.0) - ctx.ph2pr[(_i + _d) & 127]; + p[r][GapM] = ctx._(1.0) - ctx.ph2pr[_c]; + p[r][MX] = ctx.ph2pr[_i]; + p[r][XX] = ctx.ph2pr[_c]; + p[r][MY] = ctx.ph2pr[_d]; + p[r][YY] = ctx.ph2pr[_c]; + //p[r][MY] = (r == ROWS - 1) ? ctx._(1.0) : ctx.ph2pr[_d]; + //p[r][YY] = (r == ROWS - 1) ? ctx._(1.0) : ctx.ph2pr[_c]; + } + + for (c = 0; c < COLS; c++) + { + M[0][c] = ctx._(0.0); + X[0][c] = ctx._(0.0); + Y[0][c] = ctx.INITIAL_CONSTANT / (tc->haplen); + } + + for (r = 1; r < ROWS; r++) + { + M[r][0] = ctx._(0.0); + X[r][0] = X[r-1][0] * p[r][XX]; + Y[r][0] = ctx._(0.0); + } + + NUMBER result = ctx._(0.0); + + for (r = 1; r < ROWS; r++) + for (c = 1; c < COLS; c++) + { + char _rs = tc->rs[r-1]; + char _hap = tc->hap[c-1]; + int _q = tc->q[r-1] & 127; + NUMBER distm = ctx.ph2pr[_q]; + if (_rs == _hap || _rs == 'N' || _hap == 'N') + distm = ctx._(1.0) - distm; + else + distm = distm/3; + M[r][c] = distm * (M[r-1][c-1] * p[r][MM] + X[r-1][c-1] * p[r][GapM] + Y[r-1][c-1] * p[r][GapM]); + X[r][c] = M[r-1][c] * p[r][MX] + X[r-1][c] * p[r][XX]; + Y[r][c] = M[r][c-1] * p[r][MY] + Y[r][c-1] * p[r][YY]; + } + + for (c = 0; c < COLS; c++) + { + result += M[ROWS-1][c] + X[ROWS-1][c]; + } + + if (before_last_log != NULL) + *before_last_log = result; + + return result; + //return ctx.LOG10(result) - ctx.LOG10_INITIAL_CONSTANT; +} + +template double compute_full_prob(testcase* tc, double* nextbuf); +template float compute_full_prob(testcase* tc, float* nextbuf); + diff --git a/PairHMM_JNI/headers.h b/PairHMM_JNI/headers.h new file mode 100644 index 000000000..42485fc51 --- /dev/null +++ b/PairHMM_JNI/headers.h @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + diff --git a/PairHMM_JNI/libJNILoglessPairHMM.so b/PairHMM_JNI/libJNILoglessPairHMM.so index 435034f78de8b6b51001484dd15d775e3fdae892..9b6b8e6930e9764b187f019979a525dd50840252 100755 GIT binary patch literal 96945 zcmb@v3t$x0^*_F014acmSfEj{tnF$eQA`Mt$ATu1$V`}p6oOKXE+GjL$jfBILlJ=_ z$aGmtEn2nIqF?&0_3>@0*e_CQ6G(We5I_{cN5I!CQHfX}fRO*^bMMS%5|g(6zrWGR z%sJ=YbI(2Z+;h);%<@o%c3ZEgD2H`99k)0L&73GPsV2cl>`0U980F~WNOk%IauNOe(TWIE6Xy&t?ZYIEG zJ!2&3h{lumYAMclo*pxu^<-LJS)T^-#d|L^W0z-W+a-)SXpV#3z)O^3b3+;9r zJO=O6ao>sCjhilteb$HDx-K_gCRqqPC)nQk`g7br!<~-XbR&*|c<=5i1yaSm5O)^t zOx*dni*cvoz6*CU?ygrE5KD3Y*hUzQ_qjIMdcDPb`30Uq+|?%hD?B~8vvEI!dopfb zMYwOmJrnn8+{1C(uV(?Qu_3&+_wHVu=<#K@cBWi_L;A7fQb#J8;i9g2{SXaHeCz|J(jA|87-V<&!oKwJS2&j5bd6xpF4+kG z&-$h+5)n&hge%X|!&V8$M$6}Amy!Raj&*ZPynHyeK`y~RQSwLi#Wlhy+ojnwyjH-x zTEL&}wbYPst$a92(O#n6%Ot{T$3PEydjK7W>DR6AoPIsZp*#+=zNWYisM+C`Njv?e zpXm-?hI)6W&ol_u9sa6Ir94g5lD%0CM~T^vWk)1F)^v*Zh;~G#MkbcGVsK0@xwjrL0T4 z427d7zAhjOtTW5)eX+#1R!D@^?)y<+rq41JY2km_gZ=-ehyM6{5BS@Kotr|~9#_M! zbXTuOd(gv&u*2@s*P89yx>_=?GyB)(9`trC@T~9Zn$CLo%IrsVg%aOn%KDV4XLlDp zPlEsKPM*8W{<73e?>6~d=s^#E0h8|dtnWeo8%+MOUF7+whx9wl{*v0IzFW{==x4HK zO98XYe(_MW)K9JHPb_;FZQ`5Ej-G1P>x`)%M^|~^2)e7+f*$(I3q9CdiRlk(&2lYy zc7wUF+h?U9wJ48Qs%a0+Aql(X!%^FVT`|V`v6;sH*kqPl*QMOd9{l7Pv)ouSpv*!) z=k-_7?(m;mB%Er>IRoioLf^T{%>LWdMgOm(zWovLzBud}kq6Bot`t-MiKhNjP5k3M z=<@~WpMHB~mvQ7bkc;|s2PFfy$>*0n)b|wV-Lc0?KlhiXTMwhj7(?MHwpGB^ke$%b&~8@)tw=a z)Q8tX2-%&S=X;R=AouOSDWQpcJgr#e10SRn3cQZdC=7V>Mr_k2gB~@uk+}zp00MEfaQPCbB(>!Mr>E8D{*G7?z2f(B8a$--F-V z)kD4R>Y-n3HQVbs({D)bSSt~BXh9v%znlGiW|_pTHsjIz4b!5-Qi2kc%ZdJ^0nmIDaKj!yDsC)B-FP% zK8t!NcW4joYnXa#GVQQT7Vj8u>djgdvE=y}{(=3pX}wIi)J$L7L;s?AbSLK}NKZbF z6Ouub$%oTM;u95#u*yy7!Tw`=@H4*?=`rtH-I>oLJ?P=I>EGNtWcoV!a0H=G%9B+i z5veBrsvi8(xE|VNLl1WT&mQ{Ao*vR~Htl3x7e7NiUEa;SJG}>a`t^W!`2F`5mzMY| z0=eY@zu)06EGY~){Il@@DQ{seUvrBJ?=NupGw1uK7TjA{5hy61P?TFyQBdLV-NXDNkyPAP#Guy-@=l6{gr{jq6&XmZejVH;$na1WK}D@ zx2T|^A`2MB=kw1kDV$b0YeH#p8S+dm$j#RZ=M@wc&M7U;pIA7*phU?nD=G~vDl0&~ zq(qcDe0ZKr?4Mm(ROBx!FP-Jjoxd<&&Ckg8XN?LJR0Q&JD+;ovCHfsj`2`EhkZ1C= zTs1FaLQ=+j0aV~EYf|3SNdhfUfuvEhaw`h+Aayy!#@~Y6Vl{7CASo$tPHwqBP@Y>D zr~ng@g{(kI_N%28{wZYzCB>!r&=SfaYgtHOv}9OVifrHIlaZ2FRFGRvHp2xQunOb` z$OWjxioC*tl0f0?!n~=|0;9mg4{oxE(xQrttlR8pR5imtzufm ztXWb^CRw(N1g4iPC@jf`nN?I4qoRIV8SHX)Zefw*UjKL7JioMZR#8E>q}fHKxq<(? zU7AMOVURR>XyWGi1$lwea&=l@1npYQN=ow2owX=XkXBxvyC^G{HUjNsWZYa{P=W4~ zQ4EM#N5cFwO6L2ACrvB}Oqn$oY($ofqJmQcsp-fuI#6m_w>mdtT17TRnieQ4>drc)iNFek zNze$gCQVEBqsQF*{j!N1A}?j0G_4{=ylF_YAsS~1ts?e=5(YJwHjTVJFh@^oo-skY&O%{g_*e z*>rS((NhcH{otklU8B$ESR|UJbeojX z@I*gVTe`rHqR_|u1?AftC9J*&8Mz8p|F0^1A!PLA&*`FAsaN7BR| zB>fLPMGCqmyXZ(f(^;4Q-yJ+@1e7?t5K$DOHVn2IlSd;aEU85Bw=ij>-(ON%UJQR$ zh^UWkrDnk`OdEw_i>-mc-XZ?oXqY>VhD}Exg3DQmCri*x;caCN&*esY`-YOPh4eBY4d=`ARuqUC> zNw(s?M^{HBeEz&S1$pz>X)=m??ky32`mgk6Z|m=4g*M^;WA@*%(~&*e>A#74OshmQ z<&{=q__}j)QqqLdlKC*P2?(Z>Qt|{)QCM1%or@sOj4&f%?g$k#k&ZyRGhTX1VL}L( z7jnvSXVT4u6`Tb4D=N#%O3MQU`OqEk#DIL=w~DDP4Uat>S2aaq0av9 z3c;=fJRVD}sl^D*Bo zcEHnOOmtLKgeEB4gO{F z5v7QP7zm+u-Augyvoc4b!33X(f9Pgvj{4NnkSp^d&f6<(=gLzbR!xEA%G=i4{JEd z1*NmiRw*2bo?KK|f>1OMvkQdJH&@InEL%{4t`ix+gp^c6H|sZ03p6JNliYG}kuprH zK-grxN1D>CUqmYrLZk)1ch!;8th+V$knx(Fh*0e|qxg4`8oUDouw--c*^`-luZYI7 zO?3V)k$;5<{QrGxjo?~Rq!V?H`7Mk29&;2cOILaFx}S$wZ5D~xyJ*E?7m=&5xU5LF zu)kt{F;+TD7mR=bC5E7= zaxgp*!x|PBL|#mm{hZ?2d1U2f6&JFj4o|KWL%1z%9wv)eCYd_ghwPX&`O~rDAc|xz zmTL(+dsO$*R(wZV&ON&o%AOSL=l)BfDr`dCW(qZzxyXL*mAT3B9kulZVx=dhbaN~}(&SZM?Kc?k&-(!F_D{34Vj z5|#vt@(866DwC^5gcAu1ieQ+};)X%W0z^}}Q1Gk@L>*kT2dX6BF>%5Ke=;&n)YS14 z{KJP02gBPje5q3=J0{GSkv3lSCk;!M#mNk*N<#}ADE){!Y4u=~x$+~V4 zHof{dc9{Ezr2p1Nue`DlJ8#jBCL2B5MlV_NN9;&B9q&izy&N-5I@mk*|9KvEc6vKL zF>!(GAH@z?FUN5+eWH2J9!PN|7RcwQF8G#@mA2R}@YuSK zT+i9?)?O=#wKhDqIwRLc8=mLrt*g$4x6b~Pw!?u1+_D#>I2*pgN{px5 zhF@yKuQS^%3b?0i_*64JwujMsJ1)hBM@NrbSvEY|-nwSk@Z@D(Gi`XwhDe)Z!^06p zt}+`Q4lHslwBd17A#yFX;c>Jra;>!Cu|*%bR@?CQ^8!!U@Yte{TFv58Kt$u_)V!8Nu>umVjZ20GF_=z@rtqrf(@EdLT zOdGz=hSzNP9X7n(4>#HHQ*7xE+VEL6e6tPzGaKHp;iuW~tv3908@|nkzuks+T-#y) z{65FJVr+PR_hVhLHhhkS#52x@zuSg)+wk|;@bNah--b`L;b+?LqipzG8$Q*BpJl@< zHhi89pJl`6+we1Ncr(-!)R{K?Y+L#{HvGLde3=bD$A({M!x!4{OKo`bsFI+rwBhI3 z(yzARi){EOZ1`duew__(AAg^-;Y)4lYi;;48-Ak=f1eFsXTt|<_#HNUr48R?!_T+j z58ChxZ1`pyp5KC5mtn&%vXFST+VBt9@NG8yVjJFZU5EWYXv4?Y@Jnp?SR4K!8$Qm4 zUuMI*ZTRIje7p@`Wy2@h@HIC4C>wr-4WDYmue9M68(z2JvuyaF4L`$%UuDD3wBbWG z{2Uwp5gWeDhX17vztDzXZNo3M;eTbrue9MGwc%IW@V~auh+#hJVI}Z?)l{ zwc*=r`2Vuu9p*fe@y~i2KE{UsgAE^R!~fBSkF(*Qv*DGhFJg?(!QEIMgFg?xf={oj z8e+D#r#$jeA@!cM}q0$xry zn(#US-%q$V;ne~zC(MvPyi~w*3A5VaG6Byb3>h8anF79(Fhl!rmVl=c#+rg7oGRc< z!VK}li2@!^n4x{xE#NVP`w@;6@NmKm@xu-Q4<+m(+*a^t$=$G<`gu%PQYi60_Id8yjs8~2y+S$UMk>Ygc@3A+WniSX5gV+H&&VNMak4go(;m{Wsr z>v`7yS;B6@%>sUkFr9O_Nx+X1{xRV?0sn&VPYBlvcsXGY;dKJOpYXMWR|~kD@O6Zj z3V1HzpAs$;@GQddgl7u)PQup{&Jyrc!kh|(Qw5w!m{WjoqJYN}zJah?z+(vCNH|u& z!wGY05OxT7DB+t3w|*NX*KTp_Cxb=)^f5I~fHw*YF z!nuT-1pFxBS%m8Z{0qW)glh%7oNzwjbppPhZ~@`f0xl;!oA6Qr&n0{>;W7cwBFq6O zJX64T5-ud1CE%%q=Mqj8a3k2yi9g)dD_2m?3v~seq3W zUO>1^z()x$Bs^2VhX^ksoF(9WgzqPuD&Sp&A0V6v7*BC=yjOy~ryq7Zl$yFgul6P} z+^@E-=}X6r`rM5mK>su?8{aM{RpS(-bp-k

cZ-x*bL@^bRF-^(%PSLf5Xx1N{0S zk9PgXO8q&fQq>Zrj6PW5b`3#qSE}lw6utMCpSc}r?E@N+^eA3IFaLB~@7;Kfx+^Vb z=hoS-A=RdQ<7R@TV>$!~T#4kb971wPdKt=}2+Gd(X2v5sw@Qh(S{YqKmXbkQcG~o` z+tcotHeFfv1=rM7{eq^i^0a|xn*Oe)S9=`5YWg<4uSW^3@m#?)!9Lrn#hasjb!#6; zjbyQspy{WT;0BT3O5Twsrc;`kfI6p9^+xbES#YrIN^a;Nmwkt<*mE!JhP+j&+AQRB zxT*~&xC5j}05<{j)qLryepI}C;;Np@1jLLNFNa;#SK=jYS^GbukRkL{FZ{?xv?rsc zT?wucOqJj&!7W|i>{*C3(8*#v6n%|n2ei2fve6QqrFaFwPq>O zrFpjlGh%?gVNKS>DB<8{tKe~_+MuW3a*qi-p1Og^QAWFf{hIG+=) z{?(pVusz|5?fr`v9FBi{3sH@Ok`#@0+w6(E2AJzGQYoR7hmnN1W@Mp;1?>>5{&nyN zX0a#6@S;;Qje#`A(*bQhSHr24I>QpN-*VqS~Rl68FRp0)we?kDUGoJ2)k_2 z9wT3%Vpj=qH67%f9j^F#qc2Nf!AfX@Ec5=;-IQ6$GKG8z=vJGcwn#z}a(t>pZm&{E1}LQ9)HZNP0u!YHAsH8RbU!YT${@)uci2WpHou`&%jKKZ-~K0x-B z$ju%mGMc}J7&UdS>bp^D)nZRyM_?cmfw|D3Nc3Ce1T9w5IByg*!Sx|Y8z5<%6AGG; zJP$NcW!UE$p-00Fl%j7{YT&cq7)ZTBBVQSRf*p;c+joT?2Ua2WL!$zYCtW|1*507h zABk3iYef@ect3;DU!myPSH!rMufYT_d<>4kv<|BweR@NBFe;;_&a#z%+4$0YXe@DV zXg@g8wc;rBl#%>pTKkT4y~vY=F7)QRZZu4~Cp%p~Mcj-{KS6DwtsJ4!s)p8JT=LGe z*9axo>4QCn@NZQOvER622R{7=Y9`w=13RcMd>R%c z@NvL{YP^KvT=6>u1YCd36K^~T91wWrAf^T{gAJv7^F0daecy8}k3$q3UJujJg3<+O zh>M;>J++`T1uZE30gaBXLIHjUpUWEPgG`@3?zMN^4r4Hk7iJM7yH7uMTnEw^`K?yB z5jfFZvLp`OMc_m?84H}{uZ(sA93N)WwTkg!-2MY7<*%qWOKEu0a9q}~x*4OhOz=5}WztZ~m|KzLFHE9;NNEn< z0=OPt4Oy53K8@o}Dveb9QTUc+OlFVH+hB@>1aN6E^LPcqia&{b8)aNa)?g5C3`P}` zzeIe;S~y%QzQ#%z{D6Lp!Kne9@jQx!^&@01+atUtT&WU_HTId_BiMU}4WAWjye0_M zo{eCLTx0CiID`I+VQ{Ua4z^R>(!YZGoTOgZ-BDszjNwOKh(bSo7>N!d5gzZ$M>8I- z>Sy5*b-1|Ao|S-&x4vXWQ;t2uCfe*-4cvCL7#`?*Kw;p()A&rZ`oKHPc<%||kc^CF zvJE1q(GG6Fvs{Va>|s*lr(d8HI&QdS2Xx5Vk0$wen`yv%Bc9O2B3CC(Y^98$l=T?QEny%Z^K{b0 z`|wptXrNC-2@!>{1WOa58YjUG*(rdf2_gK|Ev6>UT_!Za1hN+54S>}Q#y+rsv}7$) zh&MBZD5O@Vpo}twcr#Ooc6$UVgeK~Mg(e#0V+YfKcP*aKL>6SR*~Dvw~T_o8CY1&7?4R{J5vzwZ@IBaU-Pkn_Zm_XJ-yaBMd z8n1%|q+J(DA>PasLKACc3d$&Xh&MBZ(8T>nkzD7ip@UwCdoi@J6bW3_m!N!G?f*i# zLMfMZu04}>s{Jn57;5kRV~3mwGdN>~+-B8311lgoo9(Dff5WWzqnD!Ii#-cvShf&p zIugB!oS-e1w8a)p+COOsorx6QM7#l@zC>LgK?)JIeQ11xYNNJg5{$%nW08VBeHXHRgLP-cMaD)5PHY4ZJT~>?xCr2Tuc%f{Xs-Mg3egb0sn&?HNcbDM=j^A?imF zN?VLz#5_XI#t7vfpd{B}w!k{gaIF}LHdKOmW%cTt5$Vdw2FY`j;D}g5M_ymy`(o4v zzoi%?6$Ex5ZWd!bLcOw@eOfR(DQ3JrV2FkgfoUh`>CR{kYacQrnq-n*eQB1+EmQdP zHBJR^npgsHtylw%X26sS-((iXhZGFUY}pJ9HBX#^5YZikwq~zp%*0e_?R7rq*SEP| zA3SKHp7S`ONQpO*{U_>cA3t~zl+hV~Iq}b7#OQ*LP4{jmY2gnc3RYB}5b-b7gCMTG zF3r15s%`mB#&7y@a~>|WOhIFexu18Yuw_IQjI1sc+8!x!#?6ufgEbjuBeuMTNEDs= zI=fl&K1?-3i;EqTg3)c0^z=4VRr91y3XMHu-0=xb^dqWgrm=}8Nm6^G_eBxrWCjPH zQFN!GPs5zWg&AQNOFjra8qUv5GZzE<4;r^Y9&=F&>9`^VLpD+=JyRNg zWdo9h@uKa_3RyuAM7XhzBcQ&YG229oJ*I!YbkM4`?YqmL+bD+*mDk}rBY z9djf$&I#2u^wSe*uA%Q_xc>Yl?o%Kz)<5AI+DQC|K;!)_Cj1hUzTj5ah`w9ZcdGgu zs=iy%_gLjB`Wy6ybTg_B39H4bi>mM9(k-VYs{W>mM7&kzYGf2*rN@Lcp5b}OL&ucY^*+ptSF(W11$=EQ=&y=@2ci1K%|7f zumv9NDB(SpFt($Fx(-%C*kzJcLkC)vFjt~?5RFX=dkIrm0#7lRbvVru4xv@eI#hSC z5+w|gtk!j)MF}wyy`E@Sbu)JPBX#%^O%UEqjI@%093vK5C5V_%tUU(lA}q!Ns@0M`OlL4Pe=mhpFR%qFiGx5@@h$5^r#?jBl$*2{tERm?vv#CGWQ=z zzCn`rkUZ5a8-;@TN|F;Kxt8Q)N&WfN%HlQTu$=I_eH%+ zBsqcPcuAf{@;j0|5fK)0_a*s9l01Us|CQvGlKkakAYU!X14({Dl6Ok-ZjuK}@~K~f zyhxHS0Upf%O!8Hd{4U9NO7bL0UQP0qlDv^*FUita<}|t;#w7GJ>6o}&Q~V*?e2Lg8 z9ru!~++=b%3)4fR&L@X6heh2pWwYEw@)=3KgXBY!+#<=bB%hSzQ6&EjWY-YvcfxFv zX{|DS%1`l*c*OHJJc8qD{tn7aiW(gFbG$1U!}WI*{asaWQuTdUD$PhfjK!ZDLCfHt zKxU$*tE19a&PdyqIw5LX>O|+Z)XCA?QnPz$`s7qipQ8A5CCjI0&dAVn5VfVFPgU0i z#%RH7Rq)VsERUtU0b4OLWvPcZQ;?LwEIiozRRQ(8PKeS=(JyNmg`BQP&byg2R_1(0 z<~%AMy`6$VzZVJV%!#6m`A{=zc`uQnfr*<$QTG8#%L&hgf@ZIl`@yPy07Imze=D{` z`(owWhi!-IPtcxfFxmrF8Yi3sXTxmJJa^%NOy#tl2mjlx)Yxww_?k>zB2&9Nr@kBp6OYu2K0e;sk_(Na-$MIV!6r8ymc}?Q zgcck2o-pMWY3cbSzbnZa$u3D=Bgq*gb0C4JHVMl8>(f`DIBCO0ofmx9JT@ZX)^Dl3etN$i0(f4lyYEFC+)dvL*R>lDA9pqa;rU z*~N1V)-eO_R^rS-DiBMN&e8S!L035K4xG5at_1cd5zia1f}dEIB`||_j+i~oFPFwK zLyR%J2aX!;4ki2bWFK<;o4kRd8wi| zDbBryDmZZ#p>H)_e1`?M8|&W%dnd}njv9;02aJ6_ML#5Ak4T4wV-gq8c|$<4Eo+nU zA5J@^7Tymxj^?R^l z(-3FdDEHxzNWw?1>Rm8KHF#UBnozIQw?!-7t*+&bz+ny)Sd8YyB&`0}t^a^B9Ex*4 zmge`K<-9vasoAFli#)OOQ#4EooNa-NGxZTMnR-zSN@{NnT#~5=Vqyu$Ded(*&f`|R zyUO=tBgH3pg(kKu-h=Z8D)pa7YtB7t)tT5pjH=()s_KPptPe$3o$vX)2J!d6^Wnxb za202RSS7)1Q`M)#$7Nv3)fkI}mB+hOQrXxpFPECI9mTmG4x$A#?>SX}-{+e6xzG8z z&-;1#;Er^fw|PN7U(F|#mt*P}o&04>yt+BU0~q0#aeiaKAc5~=pMDG@bHck~ zBCdwYT}u50r&9mb*vwF1kPGqwj#l+=)V(c`Y5vbt{kYG0HgGu{`WRJ@9;ybj$3WxR zL!n+Y=m0gCJ_Z_3ABrI*8^VFYrd1)K>V2cUP6-W=O|iG;V3cZvleU+8Bp zwf?MAtv@z)N~q`()N2D9dxQ-Rni!LbNFZkZBX+$%=Ymq5E0 zS_AzFBKc@O<9zy}7-5~;+-g-l?nqTeM5>~N#$KX@Cb#>%Z&h5cI1j4a9>>dzb-o~(f+|U}PI=}Ei097xE?P{Q*agRKV@#17t zm+=Dr14DusK$b!c=#HlX=T>8#3HoAfB_+JY zg)Zk_rT#1!pW#}U<~{0q_;w_PqmGEgRQi6Ls_d(J-*>I}BMeA2SF}6QU=i(f3w%05 zSn^BwhAdePte_R{qnO4AY%L`0$aVTXrU!qlnRQ5^6rOzo5xtpBb)HwE?v62jPxE(v zpm|#`^@j}Pjg`PGu_JE$We=#!8d(lfR2~g4W0HiAHN81-6I&Jb-q<BTX|AXWns`MehbKSDTTlOjQ|+|F#Ef@S9>Q8tcS z@mTnOuZwXgr$53lr#LTY-p^eR=R@N@Zv#$(sIG}ksqZqoFz=!{Y? zJhjuI%GtY7KdDNrOImu-!yP3k!Ah9sZfO|Qfb%_?hcu%1l*0WiNZB6kK^tJdTy%F| zOhQy2oQYO+JNNMTX#ms7M3*IXl|L z#^rB;CMHo2I($yVGa2YY4|d}qi83uN{G+0BtzfZdlyK7{JeV|Vdcc$Fsy>8c zXG(Aid|G7XJ^_B1tM_byGt#4-a6SC>MGQ+9zy*Ax25(Xlur7a&>+-Ie)u1p^Kq!jA zSV!&A0E+i3RyW>NT}$IgILQNuIItwnhbf_Z5c}YSd40=Roast-VbTC@JioQq@YU4K z&r;Dto$mxNdF?w)(fjtnR?{$8qt*wZ&sl^%2-t>UtkL?Qk+osiHr4td^npSUH=y4m z^yw>uv-+dGwNPK|rrd`h?VF107!IKOE0XyDFdxtdnjVIF<@7?_g?{K-aR*$GrW=z&gXcpZPE9|eBy2@L6qc)b4OewN zw4H$yUa(cH*mI_ksMPGl!UXU>{d97jmT+!Tu;`}NNeM+a!NSf%)+n5jRP;0mm=^1* zxrptB9T6xSiziM4(|UA@x+%e&n~X`p#W%HT-j5W0Zmg^NCFosEIEO=g=El#0NW6&% z?G{%u<|StkKJQlceuZF$IV4rIGh@{D?U;1I1zvD1|A=byQz=yqifxKX z*OdEm3IdbjOcH~E)(DHxJTb27PuU)babdh_)%MQ)pR@Voy2+uyIEOD-(&|en zF*uWgD%t-dou4b}s@3^5tMk`u=dybhl?j*N3~MlV#VZyUnJ%-7ej2o9OxvLN;{$&*^SQ zVR8%m{f1qi?shwZw7?J4;Nlc$U~Hl4|37?+ezM2)*lJy`Q;iKD0C|4V0D| zoDu_#VtBm^Jp}{I9nj|vG)2UkZr%<6)`V1*$HFg>6j^ByY~X$u+(F$T2sI4aUWax3 znFZ3mEbG`P=&+6{;w54otpJUy8f0qYC=GEAiZTqmiN*_$g!zi{!W!QqG6?&ih2CLV zXg+wE0SYa2&bcl!jId;Q1I#HIOqnMOiYUWxw#wATJ(S^-Z-L6knKE1`54%FXDAqbsNmd7n}$?!IxJfl>~%@=Q--JOJLVvJUrldc=cB?98jg8us>`^ ze}Usv^g@h)Tyw!Rj~gTCLVu@$`a%lUEY4ufB8)W)EedNEbsPqL`Yv@Z29tdp2DkZ~ ztz31vAyL(DK+v-ohN>@ipf6y(0;?`XiSW2Z4y?K?PQ=24lrYRA$X7G1{n$<)U>FdX*5Iz_+%2mC2~`Pg^` zDMvY!2}hG8M^x z&@#Dhiu5hB;ag_Iw|ooV@<}H}wjr<(D{QcrD9zbSl_8kZ^j!Fs*`{x4wtP!N@+r%= zEQD`q$LM$*qhroRQlaP2%|2509*0Y(LhF6b9f8ZI1P8c%`T)n2V742DGIj}&K?Zy&Nwu6Hs6{>ho3+ECU9N}CNE$3p!a>BWwwZ@JxeanchzD0#^*#d|A zj_Uo&RdcwrLWO(5(G@K?2R`OYELN$wu{e*;=Bhr(ts}g`Jk6+0P!P3Y*Xb`H(rd%5 zslkU@u`8tprx;qok%)f#DEe(M)Z1XFt6&pSzfdm1TPoj+FK=di-!M+_0JMC{Y zA1yQymf8kOHOBfv`J+U;OzPCMr!7%EnNx5H3(ltb{rVoeR}XT2wf2E?eL2jH~7Wzg|CVsRM^YQgBynjX!A zHKS1z4b0&>kR1G`La&^3Ay8`;J{u?9sjPVFQVY7-qoF#V_tQX@FX4+0pMgN-hw5p> zXYZ)>7chxBf2%Lyq>7_HoLgW@Etcx4UWdcsysiOW{j2iN7*c@!hnn63-kg6A}_^E%$+c{5%?Mnbg}S^ zSoDHd677q|4c{uP$2L~166r8j{6xH%V@15kZyf(SohBlQztVd$-Z(E_5URqF1#S^u z*C-UO^&@@?^oVfm9}2vvi19w^X8h6OQ)2PSFiRU{BnT9~H=%-X1g!5=1mEzqH@pGw zvlUCrjSgcZQn&okNJUa%uHgxy8mL`3n{S*qoP8bso^37Hzy&J~HENg63>}4Wcn21qn zWcVuUn?Y-s?r#i)R{b|wnE=keM0q^|>a9P=xVXnyh|xQ;zWl)7to0Mb$``ok zl+SgLZ#5lZrhc-c!%Rg&_7?QF$}opp^g#?2z+mATQxbFiG(qx{>jU_@8Y`#XlQ^ft zaRfU$SV`A{H>(NQR(ueM>9I<}yJ8=z{w(&QwzwX?774YW4<396CP|zMsopx*!y)=N zy@6)Kz4k|1IG%Vf%*Q6?Srw}|-lI$6H1BEGiY7WY{RG&m-nU(=>e=T)^U+nY?BLza z^t-s~gY*Lx@7Jys>ySW6_*(V;)3pi*K+rJT)PxVQZE`EzrE^=F*w9h-wsHG}D-w0{ zC!!8Yy@7AGwgR6S95I|*3i^mKn86O`;4mg;a!?!2fOZT6+Tjdn#~@zWf&pho`EK+d z=@>()VoNNJBlo0WdLmqW`l!}P!R*8~Y_^PQql&`4I3w?nX&_|4h zOi7e!G-XPnOrt4N5@i|!39*hk>#|FmV)( zvTw-{(J1tMAfa8v))3ofpKDbkuxbc4A=>5*vu*Zk-hFJF_gpL1i?(@B^&WDq!l@&) z>sBpc50-UqRjZD)YR;|6JJr2yW~6{HD|sJRcP45$*T^(j^4xoFaxj|hgFy!Elg;+Q zF4yE>4%;V(?UT>;$+z0)4eCPfYpG&8YnN-qipLdy(C8)c6{8)?JQM%tLRFiu5u zBNul6>~ihwRxY*?Lnov8 zuqSD>_;+~>Ky#kZyjbKA`vVCdChwEmyrD^mvCbgI3S+q}aD@wokDWqpIPY!265M(u|jgCeGhz0 z#gD`uFr;!KyikJ!Zc+6Cqt&2x3;Yhk0CaPT&aU2ak=f@}=eLUfAiRfx^#=3HZx~*U z*v-}(5Z#Lbg_V1Xt%b^+l07#x4V62K${j=Hv@2cg$7fKvH##a8)||VR;tIR-H>_N=j305x7jZZ$hkaVCuR5E^b3kIIJ^-sx zS|axgc4*AAT!QxWN zaZpdV?!jlH{rh^1LL?(;A|_wkjjJ|TEW_lXSwzHM+l>paSj0C;yop3>OY&%hR!iCs zMB#VLL^c+_OO!AQrPd?I0M7~6&-dZ;)Gfvfk!)%Ovb_)Wd`D)4Gbs}~$^0sk367~0 z>)r^D6c+u2MN~-4j1c2Ue1Jqt=3x=qw}qhjMd4j!#%Hr#Wj_6~rH04Kku6?i>!Rl@ z$oyL{?a*_SB~}*2dL_bR8H@e|Y8MrnLE_^PVm^u6Nwj3XKSEPTdjYhxU>BKj*t@ID z6C&A;+=pxdWTR14HN^0@8p8iXbJ^{6n&_RbXY0d*tByqJJMKadu@pVm+7Gtq%Z~8h zF|Q8~kVzuvsjU5n>DV~tbO~n$_pWgxMEF*0Hgn2~H9QQR!5GDrv3t`ce8&b_JP7e5 z82kPLC0Dg!QE&xmc}0ltuz1rV>Tz7fX?O)&WX9nRnBVpoZve&|o=NM(E7k;#8?TDj zM!r5TGN7w6Ru83Al;KBee0r--KZBn>nXcl%J^8Bma7gg;LQ&9CY*kyIzz;;w3MldJ z$PZjt8UB-#To1wAn4XLgGo)d91W)(xPk>ECzBJ4u#>ua=ehzP;5#)i3Bhc04h~A*5hzJ@;7LaD?}KzurPzqdbtJUR&=c2AnNuiHUk(bIe5j z#m)Q(YJ$FYGe3HopjU6^7f$2x+0^Lq`ry~OdW}kG-geij61Y#UjA2C{a&mRb%|#~G zW3eaNvBYC(oW-!ZuGjna()+GZ+UwLjq)@!u7r)Pq-LIhq9+eA==W*i*o-}~>_+;xF zd+>&pqfYIKW4KF8Xp1Sucx<|rwioI*v=nNMtz{pn1_n6S>)Wg9K8hR@jQm!SB9;uk z0o%|9PmBPn#WQu~0R?RmXQE>9G@hnts0IN|Z{>#-%)-M_nEQ)Et88r6;*~$s4(qV? zwcQxWcfE>s%0nc~V*DC7eL{2ub^>NUu;>+l#R0)BO7L%@=hTynNye%`jFClzS;X7_ zBZ?4~YEN?m(%@4rJL*X!wUs1T^|qHL`q4CCf|v4p7FJeCSR<8#p+6Lj)1g4+p(Rj1 zZqv&T(7_(X@*&kT)dGu)KlBpAu8fC-G( z(_e`(_JW+&zAdeNd+^e9Y`gre-!+KNwS59t8;kQ(d8-Hb4RgIW|w&2X7 znTSg?h-i-+Z7=YrSXyy>N#gJwT#gSfx{n* zRUj;8!N8~$-2pY4n?BvD|iM^fJ(72E1u-ud;JT|=| zWk{q96_g<~u0e|U7m{C=afj#kED#r`uV7+0PIG=o%JXwcH(E7*2~_ZuNT7Z|Rkc{aAEAc#N9+Fc74^8YRAS3H`ziuIQ@Lf#By+1ixVK00_jg&N!aQ z-@X$HO*oStbc_!TK%YNbkpXSc+|Y#4w<2DsxC}3gsB~s)xsC_3LIV~>v-@8* za_?A_0dD+?y>9;Xmbd=JfL_)6_)~|n#P+(C11@dHqT;|Ky4&IHRsL?bmCJz|=nm82 zE>d3=;~C}M7VUJjZ>6WLxr}xXlM~fIdxt?bPC~opZ`IVXrlB`T!H-j+-3+30~WxB)x7vgAv|3*2PrN_Okv)q52?<#IsGTip;VoX!b&Ml z=U|bl+OwJ|y<5tYBWuM&6nq0tbomg4pTIIf6@K-CnJceD_d%{F0JPj{%VKe|GQea7 z=g?TLZ{d3@YQW4$!30+%A|ot}G%42UT|@&{)8HjRDu5Z6BB8M!iecQqYWck1vcW_; z{UG)-VYmu@se=i5u}>Z(cN|T>3Vneel7P<+^2wosk(mGk646EUgJ4;?gZYSOIkn8s z?gQLPyyMust9lX(0)hqc+01985d3o3T2B*D#_2!ELI|i;4WbY*sKiEzOktH^bzl%J zzOL#fwky^=mO)hJX3pc`mj!mcXB~KQcLvi8@e51Bfv-S;mMJic)!*UB{dYn}%XD7< zFACPl8ohs%9b^$V^`U)H@dVR7${-m#2g2jlB5C-wb4X_&i*`|?D4>-!|GNo1M_?Vh zvsLsfLMIsejtMNi|4i`n1+RnmQiCPf7BM}S&XkttviutPl!2*|@S?Nhu|oG`YLvl` zOt0iM;k3ZwH%X$-6j&Rh1FVeEO52D+m!R9nO4<9U9_!8&#!Uo}Tx1gqX@57##2B%H zEcHVhuO%xh1WRTu>>(Jd6A8g8O+Lb!VwiMU14mK&=_a$$Mbi#^2ByH@N4HF&i~{U| zd4)fQA^Ffc;lF_#F>qKctnzcCuaI$#?7YyxYDxjuA*4a?5Hg4Q!a=PP4lO*Ib}5Rc z$*}#x%PbUBA-W*;$?{=KA{scyrUPLm8KWVZDA+a#X4FGwaxzwmBrwH2;>GOG=*N?k zkf}YG$`+9vAuWCtf;^V9@5muhcpkVi3U6ehMn-|f`v$NSKeXSSt26#bs9BcL^0MU( z!Y8dhg|dX>LWO>92{}}}NFi9E9W3-D3vFG;s+zil4oL~$YU8yK2yP-yq)?YwQ6CDS z6Cq(33D4S^QH)tT(Ej1UVmndBoOLYn1gh9_xZ6SYU8shG>~GtU@sIf9s=-%=4sK8% z7r~5z%{1c~A?{jf?D&rEG>**R#QKMkQ`j@X2Il}mV{hO`1u$Ut0UpV~f(jfl8tfLf zfinjXDiP^4gFQH2{mSpD3@oe zN(f@F+^^^-jXuyQC%U_zZf}1L%5-evgs%FqNaD)CaguGu<-)$qIN(#XZ$|Ye2<1X4 zzq%V*ATMJGH7MSC3qvCAE?ORbIMC{>)=$Ecb}MO|Z_G#eo$D9buY3R^;rkD3J#pG} ztcqhH1s~hJ#kDJZfPyA>RX+!LeL;NFBR-wmu6i3?%hv&iWBsc0BiCPxvH$hK{M$7B z9O|hie83$5{K{rXNPwdQ*izIF`Jzg23PA~;paA2qb1;Eq@)}3 zK+~|LhO&7EfuC9=yrbglA|uJH9Xn-{`5ioNTU+Uj>5LlF&BRX z857hqIOyQ>p4WmN558#lh{sCs-5|ap9H4C93cG-Td}RCxZGx4&7HnG_uKWtefA9?e zgoX{d{#prt_dDvB=Y@`898(i;h#s2e~FQ_=sn{FZHKtq*tIKacEZzW`?Zp2zxr z>bEA$uXwN;bA6ZnZFYw^=u8G0pK@^~-?p>e@q6Q4zR$u+E+$ymrjk`iK`EFoq}?43 zN-@J#;@PoY-8p^lNcy{Edhz{}`Tc_UdLb1vm$pDM#l>Q316NkDGj^Wm3+F)XIOBrA z=nJkUq}#to==3eZ@4*}0G+xXaJHA;^g8X83>QiRt_&Mxob>!F3tG>YY0Xi*D@v;lR zcJTot&Q+wks^6sB3{J!uJ$#~NMH=|X@{sUTqU**RXIUtO8pThrc&Z~+3ogXicMh9! z8cqZ7U_Li@|A%HMJX>h+TX<(zfW}p*cef9NKWRW@?vEb`HZ!q)db&dqVmXD z`L3)v8B3)@-`1+p06Q8eReICFd*Kx*Z&mQk>cEX(a6R6`q!>^M)YIzPlt zabQp+XMQB-H#koX9~^HSfhaAT6*D%8H}(P!PZY-$Fl?gkZVs_;pmQlfIJsHk1@%*c z0aPRzt!NB;5=4e1Ct=bp8>!5=uy#QrBP>>0EQWMu(TW0%mM2UWFM~z6TsSUr;l`(s z%@Sn1#pOLDByUrY=gH*{7MCJ&xdB|HEV<-@&l2&495<0Sg1~DCt)HJ4pQ7b^_p6v> zxh9^~oM$!f+4Ac<(rMnd1(>o!ANa0^vk4q=g~7E~4#C1{%Lr1`e+ZU>X-66yt~$?7 zVf+>XiPdkk4x5>-7^Z>^C@45El$$JZe8(o3=p^v4Y@7fqgi6MX4uWsQ(6r8I$jxN`RjOcQdLNetz0Qn%~T-X;* zIu6FpSdPc-s?HPUoI^QtDCgn8Krt(Ys5uaI=Wi$~-ssa<0CKZ4LsF4|43*#*ae|jj zyypYyN&>nlJfjlIxCna1M+3$95z*F7=xy5N$;VjF)FePQi;20|G z661l#S-Q~5aGa^fO!-dqyYaE%kG=(g#tkhd(vqX#vy~t$`3chjq2laRg($?xCmULF zf*>MQxp<+#sPI^F8D*3pm7HxE$;gFX4c8z^PH4gSk;Uax7^Ns{vsujfc+Xk}J*G4m}zKCXpk!aft$h+!Gy@vXYW_)0N;nC={a? z{-q8qg0Aip<=BXI1V`i9(th)&(}Vs!ri^mD>c9i>R^yqHy2dkQO`yEgR!YhrTJgXF zV&mC|LQBtFXvg!hGCnVS_B#QekL^7#8!W%PV_)N!IUhHEaL*27DtrsdkQu(dATuB% zGc-K&VE^h*o959QHbkq-zFi7|%4@}sU^kwN?L7=~HMT7<9zi2Dp6g%Jgz}ANpoxs& zunhf+wBYU0L>~lN*C(S$Y0KdnhwXe=Hu=(L+z*Z`!g?;1-uYcxhVI z7g1?dZ+@HB-jJamt%P2VTBHSnACu8b3Dd)w3MkN-%2H4d33g!&5DlyYs!#E35NK9K?Gl=jI8cIU}Br%xX2UwJN2uCUZswVk!a(F z`ih~GQR~>6GE}hf+&w$fs@lJGJ#v8+ui751=5230e@9*8`TjLoWM9=5>ss|tf9Ubt z<@IS*pSP-c?eVz;9?Dja&v|y02k`|<=#D5V49kG&zC74i9ri{uLmSSs z;LOmf^R?ia8Cr9mqT#I8S=?vP^nEYz`xI|mc}q)Qd$qYnShE@m!Xv96iqdOhuNIFc z3@W6J6^|?pKLhr6no5-Gku6x8_2r$%uP(&6R^^}zsUh*(oj9^JL~lBM5GU2^jb1ox zpV1V`gm*mST6Hs0W#IQ7>e3VTrBxlhDDCv24CetJ$X~(pPiUrra}hLW1glqYqbMV| zb_KV8(yE#tN~`+#p|s$t6=HMe$U_tG4MElC4>h*rRKF$qR7=jP^P=&C8w6r;>#*Gh6 z+zne(;!Z&0*T#Jt)i^z88}2<&NA&?3HeOa8sO1aPEGqzx_0;tmsl;bcGf}vfhlml< zUq9$YQ~I<)rd=RSwGg%O$e<0P9*t)Pnfd^`-Hl%t@1&4Tjp5>b6nPJ!y;>elB}34> z9a`RT8n_TQZ(!VR*yOmlkJxIf4zShSA9rmCMQ9AsAc;~V4JP^1r?L0CPzI9{p`)UvNvZNbE~$+djMx7gGU z%AOq*J)7QkJiKikf@0V5FW{zpdDzdxLd4(M@m2oQ86rJGfra$xaAJS(IUJKhBOZqf zKEQG^L)D^+lksGWOb)FThB`U4O7xA%NXPEDIz71gb$U3AJP)M@H@r?Sm{#@KLz9Cq z3YGix8_wW9hu>9wU3$h(A8L4JUXEGY`Um<)pPd*QJB;~>mp)^1P4%UEelM zAAx)9SurG>DX-RoqC#fvwVpB->{I_= zcW(n9RdMwJ-z9e;K*(-1*jPaq4H^W}4G9ngF$-BpG+;DXiKZ-JvytHD!|aBF4TvUC z)?^`1pU~PGY|zl!K4{YxTG|Gi$Zq%$Au4SVQ6r)xf*KVqAdvTe=3{rW*#%Kw-{0@O zzhuwcbI+VPGjrz5nU6ack10kf#qc$)KnqCT5XyVOlQ?4)_ z-ceU=@wI{faCwj_Q{C4ytp7y|U~Q`xih72%FAq%2e_>)q#+@f+MX!NkK%m8IqyVzO zn+mx6o5>|oM7tMlinRceiV5nSA{#g|!6o*PcuXo1kFyI-HCv`y#%iGzz5qI>|AF6( z56r-Y;~H8%d5-nNIb+Y?;f{SCTK6HzTH|~?5;~Mo)517sxTfu<1Cgd4D1YxnB=u|q z;Spn;f{C(>D(0D?cmF>5Q#Gsg(C8ZG6Q;X1Q-5b_!LSI*fR~WY$2@1m;|!O(5x_k| z3qJvYP_EdP$I+}utP{-j$BkRv&HX~t{ zhcd#ih}oD;_bXy-b@bEheQCh~kLlLDy|{Dh@d)!R7300<^~_K7zH{#z-cMp*^PY<_ zci?ArGnh~x`>qCeo3n9u!VKaals)o3ng937y&odHv4e1DVqf;Y6Z;;hcM!Gb-HNz> z!94va?sMX>f%pMdbjTFp@p{kc=iokOwUHhoXB~hhHY%Cnj-s7IsF!Wef_sL0L-Tpi zz&W0=j&|uB-A+%dc96?_9+ivnKZYr*aK|R;xZ!%Z)ZIa`9xh$o0|-M<2Lc#^G6Jq0 zYXR_)!F1P|N8h=P1_qH;-I}UBt-WnkXEeI%UD)u58ZEBFRx(JvO;6csOLBaIR-uHo@Rp9^vT?&)wETIhvEZqS^a$?uKl-Ij67a@U#t^54HJ;zU4huW-sTi zuA^t?V=C&r-uWMZXh$<4yt$i=yf(q%{QM(GezOq|Jl64Xn<+0O!iEkaefSZUInTSJ z?_ky1>K&iE5gF_C{wufJ`=i{=WfZS%^!$UAaMOFjao@n$?Obv7hZ?WTaX!*}#_?vP z$23&`A5Ysubnnm~@>tJY)vdYRRr_8u7(&YDC`F7Ju0DjCJ#$q%o^5@PTyqb6vX?a8 zO^!Drz3(`_jP(A_aT?sL`jZ~hSp8?7O+V2$yvIfg=VIn`c#!OI4QP%;dS7>(i==dr zqx{EkMB9 zv-)JsIje^0g0%Vce&H@d*3{>yUF&Mco}=98Sz}MHVOF5VO=CSV-l|zm!#pwSVy&O@ zZUipfZI8rweGQ&+Q-X1wR1%uLXK7Vyjj#rwtktqd=k@j4X4nE? z8ni~{P3oJpWv~RmY}NLRpg_#(9t`^c-8B3I+>mW&aHF2X&1D>JGZI9TZzec#!SK;$ z7!K3an`fyo9}9MS+V08g#`6swoc_5wKr`CE5vc{Y|JBB`us$eW=4|^+fF9Tr8KD14 zCV;4eO#tx&-))S)NS8Hs48WoJZjMK$iH^8Jh-jX*pLbBk$#xLed*(k!8E19_io|G6+b&vk$Sx%K-X8`%->FQv`kR)LJh2QVb3iPsTu zxIur48;Q1jG7VtX@;O3;p&6yYe;im6YqrO>k?<*Ljq#RM`P=k=m?F^K{K zVFF-h+gOD|!a3GsXyCqyo0<)z6;w>s?=8k--%L(KC%Pmk4NZ)}=CEn98a9Cja}9Y^JNBoxwG2UV#H|5ptC8t1$PwSa*I49(^_YV zJ@rcz^Go znYtHJvAGIwXHNT1=Flk_uIZV(sN+`fU>P=1^CuauGkEE3Kt|Q^h|Hmx*wZ+~bX z481Fky~_StXQsy^~s;+h$ZmN!+!y%s5S1}Xi{p904-ZM*E zalip)tW?j=GgoCA6b3RdLd9JfyK~X2kqj}IKTY6IBl(??-|6{XD!+^4cWLa-#JQRT z*KGc58K<1j?p&*68kq|@VyzFcm?P>W!oy5WKP|ydu$Rpf36oXIhtYJ zGY4-tPkp_75@z4B#$xkt?9cGuffG>YxYU?AuJjlz1gFQ~;D+>=xXfMYF$P4&Y;9Wt zosN14XBkvofOGvQcxTlN|M^0L(z`$1wMY0~ zYJN?xCzv8|kIB+Kvvq$N5i$Gm5m3q~muc0PYDHYe;Mm<6gXiszq#r%~05xy70hRoQ z2@ZcR9d2+ab8zfS=~MTYuicqU)zv);0uo#ZSQlC!x6i1V_6yU{DX!@zzdF*n9PEMX zOk1~FbzEnHE;3ad@!D}yfF=r}SGu8wpiy0ORjA%|j7TMQMHBOL(pDY64pgzO+llHL zMind7-WJ=OxsBh@GdL04%o0P>t63GAAbnv?{&Bw%S+H&Xz`#`@pd~75oRTpm0r{{nmr$X z;r+z%hU#tm7#+;=m5Z}IrYRq{(lZQSKgP&)`O1t0k7>-uufpnTR4qn3b@8vLD_gj2I!*=M7}VM zejBOXvOuQ{7$reE!_!;wEO1N)QY1kJ!&5_Nfi4+HC1F_+B8B;}^l*Lw{XVf(kUXIh z57Y9MX)HtY<0zalk5MkBa9xw6+)e>to;W2jks|KGNJc+`o-Urx!gdNbKPxF)MBMGC zB_>M9KHos}WqB-C8U1eGB57Jh;MsE$qZhH#^hB8*Prn(d^t&*Ne$y5*ypw*{l4+zs zV}l5+Y8HVyj|i+iBxz1c8bu^g7b^m56GWhK8UmS*wx$8h!G~ra8;x!d77*&sZ7Hly ze_L7yTWWSi>CLVzy-Gik^b=*;m9E!Q8^QrNvBH+N!j`W53GC?H`+as4CUVt^AUk@U zFr#W)_ceiLR2$NaUO6~+?cjN9Rs2WdKMMc)J{I%|)8N<|vY_jj1>Ms<7Gfb^-Ae$a zArRZPXg}Jsi?JW=n&t2++!GrR#(p&0e1Mu;+wa4ZO?Z+EE3yhD3ZwA~cF*h9J&my0 z)y78crnc_uP-+poO|P6WeEPQ>9E=r*o-d_>&Zyt(LVOjGxitFX^clW<4IINne{~q&GACr%qw@Ic}$v{|^_D ze+lGo05e(%@1YGsT><-}tb4L*eoN`5BP(;PZ_J4}ik{AXL+`A|;}g=?#v>~%qjMrY zpdN7SEaWBKpP7HscA*~1481Ga6*e;?`e(*_1~&EVBLnb1Loe$i?0RtaYDL)|iCkjZ zbL5)I3=Urhh&w$*_+n0nJy1J3;rzhR%oa;OrwCdpO+OLGWA-PI=g-M;vx) z^mCC2VeMD}ptWNfw=*)m_SeLZraV~m(hZD_C%4C&K7W@tEBAo+$i2rrrfGSv(?rxp zN(@s;_n+|Q z4I}cNj#=GG%xhTTU?sH!ciTH*5^FY43Ai>K0s@h%wN%!iuhw?pT|i)JwW~O$R@=%k z_1ZFy!DI->G-wxcOoO(OW16(t9Mhz&116)YE4!kRgcIyn6L(KrIjxGH#k?e?ewJ3n z&z#n+YlA|Ys@ftmt*>~`VO5-FHNXvTpums$`<1Bep7u6Syn|)&HY|&u({G^Xd|4dS z_3jSji3Q3q=0u>{t$$x&dgP-z`N)Vzd{ujQc^lqjc4v}{?LB=j7G7T|pFPLACv)s8 zx4T;y4=iuHi7A%1c`9dP)JLI6>Kgn+{gkiP;>AY2_td?N2U2&Fbfj!lVFc?Qy?>pn$G5=!h0fgL z{vj5)aoo#qK$Y%lP2MxM9iyoK9x_F=VoRrP%@fR0=!e%QqkwB#*0ELW<58f{AUg_wcsrz-0VT??sYGT0zWXm134Puf-8-I}QtsPmejnp=Us=HR7 z#tr6;JE1gawp{?8QFpyjn?%{6_fS2X?s}s>iPpZ^!U~pf_R^9LCemqLr|rzj*XZX! zh7~Vz_hR9+jh6K0SYN}zn*dlI>@~n(wlE#_E-&F3_l#Oz(!kXgp(Zy|V1!y&&ODp& zbR)wd)byMTiMS?{y$cSp{ghW@|I+)q{j|rVZ*O1gRoV}%^~}6A?iG#4l$CcF)1(c< zs6>%!lT3aKD|bqMD^H%1;>lA|EO~TYBtf-Lf)*|TA_)oFuD1Wn>$1P(&9(3LK%5Tm z;`Z0qdQ8UlqmV$%(QX;WG*hMoDOz~ql%y|lG%sqrl%6l|Ej(pP&zCaSBv0F5`vLDe z_M_hAb}V#FvA^yuYd?mR6WUK8<&m_~P#Lpz7-<<*TvHOM7q3E6N^kCkvtG>`UaG-e zgl*xUEYT-dP#%@VlNU?#JzIu-ZOJW+y#xNEh}#qS+8NtaGVi3L5z0SQTg-Q@u_H*6-R5G zHc<8MCP}M#MIpqSAFDD^AXDsz>9&^D8*yNGu9^=)JB;ne*LvCv?JqL9|ARL4DvA6$ z+X;XGRz=RNJdDM3k{o83H=)zCwC;I#WLzgLA2khQh7GB(lFhW@Y^$>dCOB6T2-@xS zWeei8aETGB=aqqwkSy1}3#1J`G*^6Yw|zI1>zr5LzGtm>d;6}no_8n4?WWXghcOdV z)n>p^Zo0=_{}M){h8q&zIo5DPQo{{N4KpOkd)(Qu{;!f5eT+tpw4TXL4cwzN*zvx` zDAS#?Cd0LjEK-KMo*D2A*9HYWa+d2YVVp9?_Mnd&eqAECxm%dA$E+ixao4gw0V^KN zrhx-B1L8ytF~eQGnyf*cS=ee?PseT~W`JoY(ySWZy3<|i<7WD=sb+?pb}Eta+ykQ2Em!x<)sC= zsYuW@p<^BsMfqDzmRhdP6Z#6E(_JsJ)%h1;)Q9zsLl-3pcOfmB9C8&FDcMuRpDW4+X=f9*E+oV0_N-uVhU?rXOlNmfp zuo8>=qtHs3-GbJ0GmbzDiw{P^TtcEV-Xi-;WVDZg-840LD{2yLuF z`t79Ag43CjElh>8tp6s-VQoJoHyAsgkb2&lRLbhxBm(ajk#apt8QW>Nph@;msc+c7 zmV{!gAQW#5-bd|cz4^soQkrLk3?Y(6ny`Us?H#bL6XOn3Q>bROLcPmsJya9^C118* zhIw^zc%;ggPn=_I#Rn7aa7V4drV$pa#`!2M+t=_Ca!q@Tj&hm0v-~&~?a^ib@n*cL z^(sQcc2TI++jZ}&nN{cEx11aTGyZPTs2BB_?g}_(%SRnHnA~>`_Pm=v>O~{Dq(*X! zu(Xs7h}E4QbmUkEhF=R84UMW5K(sx)RU{e{Un-JruHUhBlh=_x+Uq6~!g0~VYbrhjeznPe0Jfp83O^upDwWGcD4cbF>m4Wp^)6Rv4A888ZM z8O=RK{Wj((k6SSEzA-dQGK2OQz&p-n{u4F`@FmuJ>VVux^Av!a>o}6F0z(D(#)Hb3 z&CP@dG!ooP+YF{*n?WVtsX*Hd(!A%e)I!1^y7!p(uzru{-G@dU)Ki@vfsxN2bn*-u|6FSA-gookTApg%bLpoY|#Ihk( z9hM%;u|1(u&*}zCkLB2&fa2jviR}roxoj%~M%X)yl(~Jm8>mRYgLWuHVTZ!?%i~%$ zQ#s?AnN(mKrQ`Lm9SYcy;K8~i*9p$y9Cj$2)3aS70Xq~bF_b|~cKZtg(X(-w~%3P`wVvlvdXZ4*+v%vsz zEPzv_(=i+QU_H>Lx##J7vDX~5Y}oFScCA4U*c-IMt~JR4dy^cnZ;=D`Epou#A_wd( ze89d81OIGFdl?4)+^&fM`^GvU5^={F{aTOd`kNQ1q{U@;;u!@yHNFFuj_g#!_#V6t z$86X5I$_zij5o5OV5Pogd>L6b#Hb#xC(DLV?f7gsJl=<)>?j;>!vc)vV>WhBn&+^A ziNoiZ4A`P%r29&Yqd9c<@aJwL4(|j0xokjB+szBG?vRyt3LLiSfx#E1rh7lozv0Dh zg)+n-({&VPEO*ogFk^R(dX>zWyBX6$1nU5)%Wfq@wmKVkCl>BEe ztquc1?a83AVj(od)Fb(HSJ!A^)o4RSr`S+2n>JJkeK27c6s%eY6^`Gkv1;*IHI@*p z8bMgKY^p?9HL5(aYT3L%SOGD^2IOWV5urXA5w~bNnu*YZ%~<@|jxq+hQ7gkUZq$$p znbr+Pq9&sT1U6XQ?k3e>My;9R38U77Fj<>gUeEL9Kc^>7Wadc0=muhJAokvZ`Oi`J zO?ZN0Lk0FyL}C5HaRKJ$VeF%Tajf6Llm}ZaRPO-&>tOI4NfW%GLKr-@&5F$42Oioy zp)$L^gNz>z;-ftlD($g=O}(D)u|PWa(T0jed_zSu;~cd0ii^Fv4$zEq_G{UO3N!~M zKeR=U2f{#k&tXFa=_RtnH&pnhC2Ka2R$yPWzIyW(Joe2~$_*6?N)HAuk^wfEj13hu z4;`?fVo&!kPeKYT^*;7F6{db-iGdu)?ngf*jB3lXxU%! zg2{AqO`(d(%TI3 z_nK>J*|?C#hkQ@=JK}AXt!$nT<2ktTIH*+nZ?xgrZuM#!CE|WqJMMAXYxOCT<uvN__ zSk-F8D?_@+=A)a(3w^qEZ)<4+a;}Y!<~asTek% z#`2E!)k0lttABk)H)ezfW#Lf<--^3Jxu$MKP?Fm9bnhd7%$DU$>SQU2i)c)T_&QDJ>-Kv1tjEOfDr{9h=4~ zhXP3tQ{vUJtEN*Sfo|1oCJr#!5a-bs=>bkL0!2d+vps=U$Q7zrNUfB8!>&qE#2i)S% zGJ5ww{NXD`l0^Dp1k+tj@g2|}9Hg^lKUJ8n%YB)CJoNJ>{T!s959#Mvp>F<# zLbGoEZu&V)KPPAE#|~+ zXSqkj^DI%pWZ`%*DvCJ~hvr0d&@si8Cn5QB7?!RmWq)1@k*^? zy5-}Ff$6%0lPC0PtIp4Lo-(cKo$LGvT)RJ=t<$G`K)>-Pz&!VT1m~xmq~P11nsvDy z6#5*Y`G@J}6yZJxP0ZF4lyk87H{CUyq&vLaoP~0z!K){iYS<*RXmiXxiP{iM}CW%kB;ax9;v_uw{1-Aatf_FA*THWiL@6uyrqyAkeZ`&>X?dzwo~w z|M=b)7R;vZD}M$zhw;A~|F0wcLf!D4sQyC;;xH>mYK9eWmvfEDD0-RXg=F#cncRpr z)WiJhPD+@Cj#3EYB*ZH}h8Y3pK++*IY>kIHF?slbDG&Q3kDb_rJ(4*nHf)XDj((4f zX!qL)z`AsA|B`ovVt)ah7xTmIFeOUH-|?f6Q;Wl=QRQ0A)rwN6D``z`MeD(9%!OIH zlD#;*{WFv9{a3(qk4cx%TByt0TWG==>0LMHLe`luVkgnEF6=Fwsrz2~Nt`|J9gz9x z`Bh+BP6k$cB2H1*`^jBr?q^4N$5h8Fu#8xLVDEn6eh7zY2Hm#hNVZ=O%Yvyr%2RJl>B}C!YKT z8hL!3`+nr_<5mziB8V1`!kr_YmqUtJN8Oie#8CnXxx8BU3fJKMxpw83eBqF7_8@|Y(;7WTL3YG+(6H< z?#JjpL&uV`1rHFYPkEM6R;M(J7~28qvqJbdlJ2p4VVLl(Qr)^qs6JE8BWl%oJw&LF zUyB0L)mCDz<1^jO$8`yNT_1d=Td)t?&TuBCb+4{{56+>fW`(Td`%TbyZ>7F_YhCrV z=*Vlg@;*FoEA`!58wJt;BS)0fcW-SLNZyC%ZA~L&t3akv7f{uXNzPBopTRDlsolEA zXr~rU?9)dR_hwX`!Isf~;8hVR;(jLL`{(F`WY#ZWQ|p-wkTuI)TTgOjxzp?QFx?}b zpr2pjC+b(3RXf$Jsc)5cAtgr=cpU(nSF+sIEH?Z*789oj)$3jApIqu+p~f+>&GZEX zh<1{7e+M8K-e6DeZTkotY)lcS_*YY2xqy!?Siiu!?NU36wKkrW4KJ_H;6KDtHNtXH zk;m?174xEzsxQh|e=V|=yVq46)rf0aGuO|1RB~ zewzlf!SEc8>ejpnXAIN9nMJ!B!G%PK>TzC+7Ybw3DE)&CNR!kzJqp#1WJx|GLd9lc zts0wD@f{l7Iyx_!Y$-L4O!p5l7{K{5&~qF*wjHWsMoSrn)eIdJ-}K)%%n?U2tuN8Y z1BbXZQ;|%$oJo6I6Y9rLOj`kn=P2Q?)ATJ3g3(j8V6xQb}VvoaGQr zN^g&fHMPJwq4ouTq#hYZz23IAWJe>kJ23`#M&6n}-fj9tf;{PB? z1^6NUk5P|`|C2M|yFA?lg_wnQ`t(*FXn5Mg16!Uxh(IPrVEE9H?y+*1h^Y;%UUZLr zz#3pHJ97>>$Qav!&RtWXrM{yWdpp$1&rPm_x4XCa%9)qJT(=z+;VTGqwCA85Xs49l z47YtX%k&%yP|i21l+TSS&lPe*5^p4lsj<$PdZheye80$H;T%~bf`>!OOPD5E_ruWM z@hQ;=3CyA zPL^raIp$=+)nZN-)KQ;Og>>`f9#*YNrAQ|(&r|goRyb%dJkwR+ryrx?`UO zgX+gf9tu240aj-1ujU-YxHm>f~0Oy-dAH)vr*T-H*Tx zbG_I-08fGJE@smZ=_oSM?Q;JM<_=Sd3B#YK>TsQdzg0TyX9$~hIWPKMutRv}h0}Ei z|D7;shfTWqfcL+G@FC%ly$F9ym-ntNcVE|-JFx=}{@51yU@G}!FG)x2AF$rqa`0yx z_qrC&Lw=d(rNAP0ZSvi!xMe%`h<*m>yC(Xc(LsgWG1JjGIWj-AzjfcO2BmB2T$G)A zVk^Sf-@1A{K+Uc1{{R5H2fKIRDT{XxPSeT3p>a4FNt;SU_>u|Ens<*JiyKTcWK^Aq z?Cr>?!AmHodn-Pn$oKErSyx9jhTCvD(Gu)oPG~ZHM{(xmsd1-z@ zQHjG@k1ak1t0xtUp|57=!EM>a4SbLLo>o2k4!e>o@m)^3pomLmhMrKLp$-#qss>;7yzzDg!})&sKH55a54 zN6}Au1>Y<9HsqgeFI|*x&nzi&hLhW}#A?rXICyo4puZ%0ZsSGTbK75jdR~uxsNx=j z8k@7Cyu`9J-(F#JkfJ0ympMUPPFi9sdBC~YFwHR0Xr%aro00FZISfS(!`yqavJCl7 zXhnf-nc)@#o|i7QmFL^-vu9?9CZPCtRpi^l#a{|VD=oj}7Rww{5r!}`)@<2U?A6` zi*ofBmA{U->;d=RN(uq`z!L~WdytnFoqju+f2p=!+A}-E%{-D6NsNw-jK}B&H^TyY zuUJORals{J6=o?eEwD&0D8b<@xaF2b`Hmv1rKk|St}VZKnssr0`8Wf6LP3kOJio~4 zVCd=74L6Q6$k^m#E-EdxkMT2?j}l~n`JtJ2T4rTtnGH7w_32zP{_8A#&{pC|bU?8a zSOb_~7;8wmIl)&h7m4fV!_Gv&Z8+%ne|enX`^Yqo4+RY-MY-5N`O+8rPXq6Z{qr@8 z0T6)Z0Urta$1Pjakk4tPjoDNM^UVsT#Y5673gyr5;|G@urax$)%+l+hH#|^eIRusy zn@C9rH^Z+&k9--%ervN(uqfj87fboDGIKX5c z3a1m$4%h=&O@&q5+uH!x0k{t^tpxOVB`Okd7=?031Iz+! zt?BJeK{<2+<^d|Tkb~~mAs?8%=mD$-Y_10#MsfNLNFQ+FuK{C`4&WBRv__-@d^X@X zocyXh4Ssl!i}ngT0S$n)fJVS3z%syNfU5w{0X71T!@F_Kfa!o8fKI^dXOKU@I_%MN zA|3j!4$YnGUqF2VwgReG0pALKfSqmN2iW87?Ty7NREBoQ4cG~o2iUv|<%8#CZzEs0 zU-drtVb#fa9Qg%QFntw;MZ6Bcd4MB;qD%8|_T7qRnSS7t*WYYVu8A5HNgqJz^E`_Y z40=*S1d;+GE12q0DrffgqIi5)147gA?<73*Nqq)F(cF;|t*;%x199#M9x$v^c_3lE zmfoFLqK#Dpc_4MoAeGcCx^sp~I}V~#+Xcl=4igRt9{UB;mpRn$<1mtwbMYY~T0t{sSs>hcIMPiDum}P@z64-V}i7#a_&)q6UTdhV`T+1^BNN@tYA=9sc<1r2edr;FW8rQaMMXT1gxYLO0?BXT8u3k%F;G zT{mDo^$c9jV@M|pa(3Wey@AqUI;dYWKqy3nx(<(`^+NSz)kLfrpfLRht0RaXsQ-k| z1O8wLyc76+A@H@pdqUuwfNu%H(?J&vz;6Y<2T)y)6h--7Zu?@Dk*%gu9=~Su$_{K@ z>fvvV)6Y>g--=9BA19Gfr>%cIQNL&pfgb^UK@ff%_{5U^0G{c~STuH@n$%Oqed>W5 zWgh<1L4OYPWeBU+5j`t$BeN6h2C`NU!$(E|icDZl1Zx^Q8DnlJ4v>`iwjn)^j%0e* z08A|UchWocHFPq#D4z#`j}mx>#1HAqN#IrB>!2^+P*<|R;ajN}bu9$@b~sghTb!uO zs2oS2J1Btv6Wp#-fX@rUQ$3FZejf0R2&;F(Ns#q?3lO!uX5Cs=r>ONCf!xO`^<_r4 zpWGYL9;)YPBaa(Nax{T&Lr{8TAIa~k2fh_y^#r7p>7@g4F}>o(U1}Gl*G@i0zr=x} z<8VmlP9mK&^mXcbN{1};<@e)E>sM*iFQI>5x5`&(`dFG~669;ztRPmikjrm}6=Y)i zlZP(#)Y88C6OU(3;7~@x4erLobx*gevK; ztr@7^1t~-inIJk~!^NF4W0iKzK<<-Ke(jJywo=%$6XdLOd7FTb0^Yzd{D;c11$gy` zz>_R~S!pR+I)BVJJqEgy*h0UGvLopF`w(g&^)>xm2A&+VvRHtp0&-3W47Wd_xeP%3~k!)hk3f3B9Tsz&un@ACU^todR8D ze>&f07m`nVR;kar*1G#R+_vzMq0=ZwRVqvNusM zK=mQ;@xa#x;i(^*27EQ}X$XhugQ&^(>w}1#(yJ8l0esguglL|n5_}B^tKTD~V``>% z4^SzZ7~oTHCi(}^+l74UXXwOm5uX;|3nZW8z+Z$<0q9SHejeyoA*}8sdS=Fq%y9Yr zw`=iOI&NrW{k$gLs*JTNd_59I(9%817l?Uq4N2ZK%&V!TedOZpX`P%GSugEMU_Z-* zf*VEeOZ)Ap>~V!XjT8NkUX~V@3O*0IT=#%=5n$bp)M}&E1d@@}KYD;z%zvR6eAfts z;(_0X_dtz=6*fg3jW9e4r+9t@&ztG-Sbu(eePiG_oi#L{oorTllM8DnWk=4FZDw|| z7IxBuv>G6n6Z3Xu5MDh1eUP%z_$DfX>>l-L`hf$MD(dwVidL7d>Rh4MsY};(u28>E zs3>)5&)X~X!_=jx-(I0!jnUaYW-VW4zx&zm0j3D;>=!d*yr@|MjCWriL5aj85zGLY z2`EgHa5tOX;fJ+@Y9u=1SzdmGql@aJ0r;)JQ%@o<%6C2RwRqpRmFBIE!X_bhgYAKw0v$*q|0 zObf^#<+~m~M{p2^-0M&Dq=z&Qd+_-nJ-i7Ipodh=Co$-s&nC!s^53$)m^6LnkVQWK2I<4*ll12#_&EO)q(8!r;jz;D zq)9V1Z8fZiOV#ckq&%T&j}B5ctJ=+x%HP%HYa^BITEZOEYCj*Oyu-pDX^HaZ1NR9X z?tQXZqxqetK{N5w!O9n!XEe2gmF5UDZay3g^Z)4(t!K2dVW{?l(aMgY+BXxGT|@t* z!Od@mZPz?|lk#OWJ!#f$*F2t}td60ZzsGFXoJ>&GUr9HfE4OR*k5PU(oNitm{u@p6 z&C2TUT^NYF-x;)z->B>`Xn%jB@|r=r_eSMS7Cz4K|2wLelKC*6(mxxog+M^P-RPPD?~bocP$9VYFjl*+U*S*Wj#gtN1u53hN8H7pZqG}r!<=X)qE0} zd|gZ{A-=|&$@{J=!U$b zQ$A+l&H5i;W9#>|Z+t(Z_xnR!*F^mOnwws^MtSQR2-tPa7zF=!1U=k^-w_e#M-a(R zM^gA7Benk?8F6+bVXCjC@c&UJkqw@0lto}ZbQWe4Q`$vOp#wEE%cS|RT}+p+~1_lP*MT!3w-t~jb^>(I$+jnw110L>IT#f z_;85wzN-D_70P*4`>!jM$F&sxz4nymO`Y=gAg%j~h~GwP+hQVW2WxwD5w8x`0*^ZR z%@xY#A#n)5J46c>zZ(jse;#@}g1?CxcRE@*95n&q#%S%)Xyql+!;ShOztu;)qR*|0 zQ9g>H@VP6tX+RUM%mT|dFdVIk9IIWRJRF#>pK3HKGJ*KLM!Qdq_}KvM-&Ezm0PVlD z5x^!A-qZSG;Qehu_qPY#&kMR=7<6A6biX3#z9#7Y$)Nkq;+~vuy2`LJPZx&2 z>`MKNL)ad6v7>_!LIJZY1FerPbja)~69J4g*!890kC86BX!VXRax3XdxPg}!^+a?v z3PQLQ>>_u8F2P3WNaptvpTHbx>r1zzYIKr*Tf#3YaM1bOGlI zxIn-X0apsRPQa%Gd|tp81l%v+TLOM0-~|DrO$xiN6);i2=>pCbaDjj&0r1zzYIK&k*Sgm?+?M0p|+1K)@0KR|>dJz^4R!UceUw+%Mo;0)8am z1p%YUQ^$3!fQbT57jUkC3j{0?aHW9j1bj-s=LLL0!2JTgCE!N_UJx)kU8FByqJYx{ zoGai00ZRm2Dd0K*pAztS0bdYszkqKE_>q7Y1dKL|^aV^5aJqnV1zaFtiGV8wTqodD z0zNO`3j*#J@GSv967Yh6(X&MQ0wxMLUBI~lE)cLpz?A~76YwbkpBL~20rv~|mVh4# zctODE*&=-b69t?u;9LQTY5*r?zo|EkVsL6j$%7@OKQ5X2jo<9)|8C3F8DJbIsyM7(oY@pUF3gK^3)0V50U=% z?L33h(%=5wq>uKO z8nXSRj{8pXkM@@uvi)uUZqlEG^e2TX|J3o{LH?5`Cr_pSQ1$=+ar&u=-+lh67u2w> zP@~MAIrA1n!tA;Cj4`AnrX)@_OfV)*FeWD@8xrouOA;A)qmkjqr;Jg?7uX8(E9_2t z(V|=MzpP|&sl#aq#4anb5N=XRK_B#G5j&O^+bsn}jsxalT`*l31|31hF`DmMe?~Z#_BiW@Mim z3!atR?D-Tygv#trC6QipR1%#wyo;Jxh#NdBEx;?AN}_GCrLa7|*k)N=fVlDwOs!Un zZJE_p=CnXayG==?mq{HEfrBMhkP{0^58Rk+`Nc(6L@h-s_(^0*^JEs`ozO&UX)(QG z!@a0)_InMY55m9n#rTe8?4{<4$>#b4Q&0nM5T;?X7XLE7oG0)>5g|gvljC*CCk1#K z`pfuo9znuGIv$beiAKhkX-Hp%|Y? zci`4eD+A~b zmwL4yfnXuyN%(P4e1jNA8*UQmhiQL;o$@&X#7idAD*T}4L>|A314*tJd?6k<3lK!Y z9?19;f*uClDzuI+)8-uc@6jJ{%7~x{LANaJpeDu`EKcQ=8Pvhy8j1f({L6Bac8%tX zL#5xG!{cji0ztqPoPNNge!&PxC;QRBhY_$gvVZK0k5FX4*B7rUvOnvK*DA7~>WjxB z%MSuEJRE_ZUiK@2Pa{w&vcKqyAMEQ7`r?Nu(!Te_4^^by?u(C7q&@D7AErn<+ZPX` zEA42&g9t^ZNV^z-M*Zm(Y47^tV-#u6`r@xpq@C)EzfzI*r!PKMk#?moez+p-MPK|? zX#b-EQ7jyR)ePCb10P0UwL-SrzWA#Z*&h4iY1d8Pi}Z2;JgMOFr}m{PpY-;n=6gXj zXnn!uLu+FirGNRPGJOB?p=nP_ca`Y3WjgmWdcXX#-H^YcQLa{Y3p*p*<1)cV$&eg4 z3@3jz;}fG~2d!WH55wyer|1VIw_h`SfBDJ1Ao&&1?@RhN1VWYDdB(@Dx2Hq|vQrw3 zUk}p-ektLR-^D)|#W1oTImGbbBN+Ti(7z@4vBF7xR;n{&UG+;3a(wjHw3t+!#ckEbwxkQ_|ll z@CGseC;2}l@N!;K(z_-9Ao@Q@{sJ%QUy}UA{F>zdq2w>{l727>mdPJPKUUzk2hqf7>KZyR0JQANP z@UcPojRJ27!gmY&IDvm!$g>y*kob25(eD!YBLYvGFzKSbbwuA2M1L#rWXI$_1_KvY znN9St8+KuT1X{U=;fE;(asY9y0-S^P(rAeu6H^r%~X4#L-Ft@rRt}gq%eJ z|7VtNobs%wcPYSWxhtCYauzi1Rvp!D${@uRnGSl zJ$7NN!Y^G0_KR2k1s~PG@nTv;`HjHG2H~Zgavw{uoQ*+p9u6bV`$C>Z4Nq5&dnA9k zKPH%eQxN}ij1T$+QSTyKN?a7>_mkdWzbKL6`}@%u z;5UcCw=#VH@m+fu`pz);FT>ysC^VMepnADS;9F?|6IUzyRg@3ur7UJXNk5_l@Ny49Rc z=J&=}j&CjE_zn(~TLj(^RL*wbLzT}T1pO*OZxDPAgrR>U4F2;l`0vB<#(9=Y|HVM2EB+o(ML&-A^ z{YfbN%rN+S7#{M7bfr9-fG0V#MgPU#&x1^#2>MPp7tkQ&91I6LRJtR9zh2M;f+>uC zD9$Y6Sefq6!tnW{px6I`)6@Hmbe$3S#uxd08h00zM3_!A?S7XDc*<8tQ28%s^l{1- z;U}mfrc2NpMfs-*{NI2NmEV^cpR1L%LH$UV;A0T|ph57_VoD_xpV49PX5d5RD-U?4 zw}QVc&-IKC^tqKM*eufZ2>ND`FN46p$>_yR0Js_sWGFe4!{F^<@NK}8oQl)jF;%L3Vdr&J-UFW{-|yfr<3`8O3>?_9N)-+a)i-~n*cBZ6Su^t zwUr|~gQjQBGIp^;B<7J16{uZK2*JU zfY-ynZ4UB#I|QFPk*?H}t}uL}t_#ioCg4e)x}fsU3q!w6(6fa*$lbxDSClZg%Qz)F7Y4z@0^cn3T-NW6Fv3LNTFvPtel_r+hnzwE%egRou8j{Z&$KZ3M}enwJ4$)F zqWF}Z0?%HG#Um-t1xeq;=~r=}Ts=B;`!WC@syuH2p3+r5^8i9$1PBmf>z< zlEU}w;5gIb(5){AaO!s%A|>w2`nR4i*+&f zuj7jo)(0(#lZ~m0?=iAzwb)D9S?(29CnHImXiV}`P)TqSijHSS|1;ksY%uf1o}3gu z_T;2X#hz%K;^*Hl?Mq2M`67}}zNF+6lO~5xJ86>tS;%}(oG{T(5iI$H6#ui3u_sLq zpZ285mr8rmr0}ISY0{-iZBj~j$tR^;Qt}Dm>wD6KOV;<4@cEuNqvY79_BWIE&`;MGsi?m;t5t+qf9DI?d_`dd#xx}2~nCE9ZH^(`N zA77oB<4mE0$1}5&k^;UjlbuiU<)m25=1Ju?Iwsm&jJr^*3&OLuT$($jG6|_nwAyX? z<>sZn6aqv{mB(8Xu@S`SNuddIE+fHjgNMIpCi#mdK=8|vCn=@x_gBnwr&ui5<7=@l zTb4B0Vkx2X$?rDrFm$sq;

wBpMTwCQOD1 zQ>>*WOXrnlmN;`9zO1FP6V=g9zG{M?^EE*SDYnCG_HpBt_(S$#9t)(mL1tzy(kJxQ zd{lJ^DnFLKJ`h-v_XTLDWErk8NvXoc&yfU|uey#Lm^-Gq{t`?w`p@16YsVs45&27( zSqdvktn_^!`Z$p@zXadnVc#X9)BF>&EE9_C#zK2(zLQ)Vn=>&vbt39%!US@S>2qC$ z6>z@G%1aknz^fp$z&y|5ukUP_6koxIiQ~vD3>SxL$6i!o3nL0Tko0@8_q{L?t$Eop zOBr_KmzLm5LeAxurSKnTXWco&lx4Yd)~p<)FwZn2%S>Z{0!OK3aehewHq?s&fpE^# zbLX1oWX?qP`s;|VLP$%(7eJ`Ygywgt2A(tpHVfaVu*@s7pq~OPNP~sqIPSi~*Iu)&*-MIwSem7Fhq)k#0q>LPMUQ-^b+M%c?>1OcD$>$^ zXudlq^Uk?nGvmp~c!5pi*=Ej0-&oAE4Wkz#7jttQGtth1WV>%}(v+;y{DOH!#kNe} z7pBUo!J*nwu=tKfkt{sR1o#=G*K?UEpgQi)E@>ug6TaRx6Q7bunn;FR{HLoSJ@h$@=lHWZaQK7(aYx1Zo5WDZf-!PCDe9aiB5(?Mp3p+a4%#z=vR;U&FU< zkc~TX9242VvA;r<SVJFd&{x6mie!T8mNp(|RvxfqQEi6>3&S2ZEbGZ7}U zu3dV90p>f^B0hKms=%%pw*~g>{%s`30WSexxLXRBHmJ}o_#_<~kEJlb$SxkqR>$6k zvcMV3x8U1-WVq7OV9!i5GA&7o$w-TeF@-tGA(Hf0bqeWfNK??SzJ2EWVrKSh)rDV? zCox6tmvleny{HI&cZ0bq{UWpO2+k`yh@eu%XL4+I6gM2LVq3Ap=2W1+fhcPk&0Ddz z>dH!gT!`_M|AsmeZdc&}JA$i{^Kqa;lTl-3#Gf(ymYhTgLb?DOv#iJ&l&er(^QCNy z=(76_*;pRQqYtzMe$D9D4>_nC%5mBq7~ z@i+5+Iw9Rf=3Dq`VFDA7eAAhsI-~$2$26b1wxF5{QCcFYq~P3Hzh34-_Q(Co)ZdaW zR#y|@w~|Gt!BIc?fGz&!y_x^3qg1w&#b;qBOvI<=(7hGIyf5V+ft^{7Fa{{*?8%lT z?2~rci&^bo{)7Xo#o!{5)ezRp%W;-FLXD>QSS3K)(B&{~V^bYw6w}?9_khm?&7+Br z{u<%vuPBA(He26@_Kl}>{eA<_zw`uT#xHkBZk59t*0rVLvtcklDHAM?in6lOa!k>a zTPF>Z4R~ic`a)q!(8I6~jQ+ot$7~FM@)s3h=uyNw{+y!8@G)udV$Qb84$o%+zLgOz z%5_3=1@pxMTzD%Sb&I3u?kRKN)x)q^X2Anuz92dCOB8$!t*E4LIRv(ZDR<-RdKh+H zvNbF%Qs$WEV`XKo*^*_RJ9}P+PtPwtL4vV3&3hzU6o+}U>OarIr1Xn(8BRSPsj?ni zOtM|H`Z38{>ag{%uMp$Jf`Ab|A14Y0Op+S$)oQkXKLSUv7~h%f+oZ+%8s;l37y*bc zJ!2J(KCEqnY4pz)gqa}-GB~cU;sF32?{xG6!!eU&+>`&?iI5$Y08`1kF&OItNL~RwIEraWhiV5K5|QQZkQs+Xvwe9Vm>LPXHIh!cNYa4uTZ1*{u89E ztOBP2gV{jEzfws~7T;g(uPxvp%|VzBLaZwxr3uWs!@}d+y|VR7TY}p`2%>iM<@Oy%0+(L3Lg7i(;`MOHe09&-!#?{|}kU BX;J_H delta 32353 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 KbhT`e9QS{C2kt%q diff --git a/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc b/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc index 59d00928e..432a528bb 100644 --- a/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc +++ b/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc @@ -1,44 +1,25 @@ +#include "headers.h" #include -#include -#include #include "org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -#include -#include -#include -using namespace std; #define ENABLE_ASSERTIONS 1 -//#define DEBUG 1 +#define DO_PROFILING 1 +#define DEBUG 1 //#define DEBUG0_1 1 //#define DEBUG3 1 - #include "template.h" +#include "utils.h" + + +#include "define-float.h" +#include "avx_function_prototypes.h" #include "define-double.h" -#include "shift_template.c" -#include "pairhmm-template-kernel.cc" +#include "avx_function_prototypes.h" - - -#define MM 0 -#define GapM 1 -#define MX 2 -#define XX 3 -#define MY 4 -#define YY 5 +using namespace std; class LoadTimeInitializer { @@ -46,6 +27,54 @@ class LoadTimeInitializer LoadTimeInitializer() //will be called when library is loaded { ConvertChar::init(); + m_sumNumReads = 0; + m_sumSquareNumReads = 0; + m_sumNumHaplotypes = 0; + m_sumSquareNumHaplotypes = 0; + m_sumNumTestcases = 0; + m_sumSquareNumTestcases = 0; + m_num_invocations = 0; + + if(is_avx_supported()) + { + cout << "Using AVX accelerated implementation of PairHMM\n"; + g_compute_full_prob_float = compute_full_prob_avxs; + g_compute_full_prob_double = compute_full_prob_avxd; + } + else + { + cout << "Using un-vectorized C++ implementation of PairHMM\n"; + g_compute_full_prob_float = compute_full_prob; + g_compute_full_prob_double = compute_full_prob; + } + cout.flush(); + //if(is_sse42_supported()) + //{ + //g_compute_full_prob_float = compute_full_prob_avxs; + //g_compute_full_prob_double = compute_full_prob_avxd; + //} + } + void print_profiling() + { + double mean_val; + cout <<"Invocations : "<(&(tc_array[tc_idx])); - double result = log10(result_avxd) - log10(ldexp(1.0, 1020)); + float result_avxf = g_compute_full_prob_float(&(tc_array[tc_idx]), 0); + double result = 0; + if (result_avxf < MIN_ACCEPTED) { + double result_avxd = g_compute_full_prob_double(&(tc_array[tc_idx]), 0); + result = log10(result_avxd) - log10(ldexp(1.0, 1020.0)); + } + else + result = (double)(log10f(result_avxf) - log10f(ldexpf(1.f, 120.f))); + likelihoodDoubleArray[tc_idx] = result; } #ifdef DEBUG @@ -353,5 +389,14 @@ JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPai RELEASE_BYTE_ARRAY_ELEMENTS(haplotypeBasesArrayVector[j].first, haplotypeBasesArrayVector[j].second, JNI_RELEASE_MODE); haplotypeBasesArrayVector.clear(); tc_array.clear(); +#ifdef DO_PROFILING + g_load_time_initializer.m_sumNumReads += numReads; + g_load_time_initializer.m_sumSquareNumReads += numReads*numReads; + g_load_time_initializer.m_sumNumHaplotypes += numHaplotypes; + g_load_time_initializer.m_sumSquareNumHaplotypes += numHaplotypes*numHaplotypes; + g_load_time_initializer.m_sumNumTestcases += numTestCases; + g_load_time_initializer.m_sumSquareNumTestcases += numTestCases*numTestCases; + ++(g_load_time_initializer.m_num_invocations); +#endif } diff --git a/PairHMM_JNI/pairhmm-1-base.cc b/PairHMM_JNI/pairhmm-1-base.cc index 90f95aeec..a3b5e0ad5 100644 --- a/PairHMM_JNI/pairhmm-1-base.cc +++ b/PairHMM_JNI/pairhmm-1-base.cc @@ -1,29 +1,15 @@ //#define DEBUG 1 //#define DEBUG0_1 1 //#define DEBUG3 1 -#define MM 0 -#define GapM 1 -#define MX 2 -#define XX 3 -#define MY 4 -#define YY 5 - -#include -#include -#include -#include -#include -#include +#include "headers.h" #include "template.h" +#include "utils.h" -//#include "define-float.h" -//#include "shift_template.c" -//#include "pairhmm-template-kernel.cc" +#include "define-float.h" +#include "avx_function_prototypes.h" #include "define-double.h" -#include "shift_template.c" -#include "pairhmm-template-kernel.cc" - +#include "avx_function_prototypes.h" using namespace std; class LoadTimeInitializer @@ -36,85 +22,6 @@ class LoadTimeInitializer }; LoadTimeInitializer g_load_time_initializer; - -template -NUMBER compute_full_prob(testcase *tc, NUMBER *before_last_log = NULL) -{ - int r, c; - int ROWS = tc->rslen + 1; - int COLS = tc->haplen + 1; - - Context ctx; - - NUMBER M[MROWS][MCOLS]; - NUMBER X[MROWS][MCOLS]; - NUMBER Y[MROWS][MCOLS]; - NUMBER p[MROWS][6]; - - p[0][MM] = ctx._(0.0); - p[0][GapM] = ctx._(0.0); - p[0][MX] = ctx._(0.0); - p[0][XX] = ctx._(0.0); - p[0][MY] = ctx._(0.0); - p[0][YY] = ctx._(0.0); - for (r = 1; r < ROWS; r++) - { - int _i = tc->i[r-1] & 127; - int _d = tc->d[r-1] & 127; - int _c = tc->c[r-1] & 127; - p[r][MM] = ctx._(1.0) - ctx.ph2pr[(_i + _d) & 127]; - p[r][GapM] = ctx._(1.0) - ctx.ph2pr[_c]; - p[r][MX] = ctx.ph2pr[_i]; - p[r][XX] = ctx.ph2pr[_c]; - p[r][MY] = ctx.ph2pr[_d]; - p[r][YY] = ctx.ph2pr[_c]; - //p[r][MY] = (r == ROWS - 1) ? ctx._(1.0) : ctx.ph2pr[_d]; - //p[r][YY] = (r == ROWS - 1) ? ctx._(1.0) : ctx.ph2pr[_c]; - } - - for (c = 0; c < COLS; c++) - { - M[0][c] = ctx._(0.0); - X[0][c] = ctx._(0.0); - Y[0][c] = ctx.INITIAL_CONSTANT / (tc->haplen); - } - - for (r = 1; r < ROWS; r++) - { - M[r][0] = ctx._(0.0); - X[r][0] = X[r-1][0] * p[r][XX]; - Y[r][0] = ctx._(0.0); - } - - NUMBER result = ctx._(0.0); - - for (r = 1; r < ROWS; r++) - for (c = 1; c < COLS; c++) - { - char _rs = tc->rs[r-1]; - char _hap = tc->hap[c-1]; - int _q = tc->q[r-1] & 127; - NUMBER distm = ctx.ph2pr[_q]; - if (_rs == _hap || _rs == 'N' || _hap == 'N') - distm = ctx._(1.0) - distm; - else - distm = distm/3; - M[r][c] = distm * (M[r-1][c-1] * p[r][MM] + X[r-1][c-1] * p[r][GapM] + Y[r-1][c-1] * p[r][GapM]); - X[r][c] = M[r-1][c] * p[r][MX] + X[r-1][c] * p[r][XX]; - Y[r][c] = M[r][c-1] * p[r][MY] + Y[r][c-1] * p[r][YY]; - } - - for (c = 0; c < COLS; c++) - { - result += M[ROWS-1][c] + X[ROWS-1][c]; - } - - if (before_last_log != NULL) - *before_last_log = result; - - return ctx.LOG10(result) - ctx.LOG10_INITIAL_CONSTANT; -} - #define BATCH_SIZE 10000 #define RUN_HYBRID @@ -129,45 +36,55 @@ int main(int argc, char** argv) if(argc >= 3 && string(argv[2]) == "1") use_old_read_testcase = true; - testcase tc; - if(use_old_read_testcase) + if(true) { - FILE* fptr = fopen(argv[1],"r"); - while(!feof(fptr)) - { - if(read_testcase(&tc, fptr) >= 0) - { - double result_avxd = GEN_INTRINSIC(compute_full_prob_avx, d)(&tc); - double result = log10(result_avxd) - log10(ldexp(1.0, 1020)); - - cout << std::scientific << compute_full_prob(&tc) << " "<; + g_compute_full_prob_float = GEN_INTRINSIC(compute_full_prob_avx, s); + } + else + { + g_compute_full_prob_double = compute_full_prob; + g_compute_full_prob_float = compute_full_prob; + } + + std::ifstream ifptr; + FILE* fptr = 0; + if(use_old_read_testcase) + { + fptr = fopen(argv[1],"r"); + assert(fptr); } else { - std::ifstream ifptr; - std::vector tokens; ifptr.open(argv[1]); assert(ifptr.is_open()); - while(1) - { - tokens.clear(); - if(read_mod_testcase(ifptr, &tc, false) < 0) - break; - //double result = 0; - double result_avxd = GEN_INTRINSIC(compute_full_prob_avx, d)(&tc); - double result = log10(result_avxd) - log10(ldexp(1.0, 1020)); - - cout << std::scientific << compute_full_prob(&tc) << " "<(&tc); + baseline_result = log10(baseline_result) - log10(ldexp(1.0, 1020.0)); + cout << std::scientific << baseline_result << " "< +#include "headers.h" #include #include #include @@ -7,11 +8,11 @@ #include "define-float.h" #include "shift_template.c" -#include "pairhmm-template-kernel.cc" +#include "avx_function_prototypes.h" #include "define-double.h" #include "shift_template.c" -#include "pairhmm-template-kernel.cc" +#include "avx_function_prototypes.h" #define BATCH_SIZE 10000 //#define RUN_HYBRID diff --git a/PairHMM_JNI/run.sh b/PairHMM_JNI/run.sh index 8b8d44b54..495a3761b 100755 --- a/PairHMM_JNI/run.sh +++ b/PairHMM_JNI/run.sh @@ -3,8 +3,8 @@ rm -f *.txt export GSA_ROOT_DIR=/home/karthikg/broad/gsa-unstable #-Djava.library.path is needed if you are using JNI_LOGLESS_CACHING, else not needed java -Djava.library.path=${GSA_ROOT_DIR}/PairHMM_JNI -jar ${GSA_ROOT_DIR}/dist/GenomeAnalysisTK.jar -T HaplotypeCaller \ --R /data/broad/samples/joint_variant_calling/broad_reference/Homo_sapiens_assembly19.fasta \ --I /data/simulated/sim1M_pairs_final.bam \ +-R /opt/Genomics/ohsu/dnapipeline/humanrefgenome/human_g1k_v37.fasta \ +-I /data/broad/samples/joint_variant_calling/NA12878_low_coverage_alignment/NA12878.chrom11.ILLUMINA.bwa.CEU.low_coverage.20121211.bam \ --dbsnp /data/broad/samples/joint_variant_calling/dbSNP/00-All.vcf \ -stand_call_conf 50.0 \ -stand_emit_conf 10.0 \ @@ -14,3 +14,8 @@ java -Djava.library.path=${GSA_ROOT_DIR}/PairHMM_JNI -jar ${GSA_ROOT_DIR}/dist/ #--pair_hmm_implementation JNI_LOGLESS_CACHING \ #-I /data/simulated/sim1M_pairs_final.bam \ #-I /data/broad/samples/joint_variant_calling/NA12878_low_coverage_alignment/NA12878.chrom11.ILLUMINA.bwa.CEU.low_coverage.20121211.bam \ +#-R /data/broad/samples/joint_variant_calling/broad_reference/Homo_sapiens_assembly19.fasta \ +#-R /data/broad/samples/joint_variant_calling/broad_reference/ucsc.hg19.fasta \ +#-R /opt/Genomics/ohsu/dnapipeline/humanrefgenome/human_g1k_v37.fasta \ +#--dbsnp /data/broad/samples/joint_variant_calling/dbSNP/00-All.vcf \ +#--dbsnp /data/broad/samples/joint_variant_calling/dbSNP/dbsnp_138.hg19.vcf \ diff --git a/PairHMM_JNI/template.h b/PairHMM_JNI/template.h index e70784073..52a4e4650 100644 --- a/PairHMM_JNI/template.h +++ b/PairHMM_JNI/template.h @@ -1,24 +1,14 @@ #ifndef TEMPLATES_H_ #define TEMPLATES_H_ -#include -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include -#include -#include +#include "headers.h" +#define MM 0 +#define GapM 1 +#define MX 2 +#define XX 3 +#define MY 4 +#define YY 5 #define MROWS 500 #define MCOLS 1000 @@ -130,6 +120,7 @@ struct Context }; + typedef struct { int rslen, haplen; @@ -182,7 +173,8 @@ public: return conversionTable[input] ; } -} ; +}; + #endif diff --git a/PairHMM_JNI/convert_char.cc b/PairHMM_JNI/utils.cc similarity index 84% rename from PairHMM_JNI/convert_char.cc rename to PairHMM_JNI/utils.cc index 01c5a5137..5b91fc7e9 100644 --- a/PairHMM_JNI/convert_char.cc +++ b/PairHMM_JNI/utils.cc @@ -1,7 +1,36 @@ +#include "headers.h" #include "template.h" + uint8_t ConvertChar::conversionTable[255]; +float (*g_compute_full_prob_float)(testcase *tc, float* before_last_log) = 0; +double (*g_compute_full_prob_double)(testcase *tc, double* before_last_log) = 0; + using namespace std; +bool is_avx_supported() +{ + int ecx = 0, edx = 0, ebx = 0; + __asm__("cpuid" + : "=b" (ebx), + "=c" (ecx), + "=d" (edx) + : "a" (1) + ); + return ((ecx >> 28)&1) == 1; +} + +bool is_sse42_supported() +{ + int ecx = 0, edx = 0, ebx = 0; + __asm__("cpuid" + : "=b" (ebx), + "=c" (ecx), + "=d" (edx) + : "a" (1) + ); + return ((ecx >> 20)&1) == 1; +} + int normalize(char c) { return ((int) (c - 33)); @@ -184,3 +213,13 @@ int read_mod_testcase(ifstream& fptr, testcase* tc, bool reformat) return tokens.size(); } + +void debug_dump(string filename, string s, bool to_append, bool add_newline) +{ + ofstream fptr; + fptr.open(filename.c_str(), to_append ? ofstream::app : ofstream::out); + fptr << s; + if(add_newline) + fptr << "\n"; + fptr.close(); +} diff --git a/PairHMM_JNI/utils.h b/PairHMM_JNI/utils.h new file mode 100644 index 000000000..8c244333e --- /dev/null +++ b/PairHMM_JNI/utils.h @@ -0,0 +1,13 @@ +#ifndef PAIRHMM_UTIL_H +#define PAIRHMM_UTIL_H + +#define MIN_ACCEPTED 1e-28f +bool is_avx_supported(); +bool is_sse42_supported(); +extern float (*g_compute_full_prob_float)(testcase *tc, float *before_last_log); +extern double (*g_compute_full_prob_double)(testcase *tc, double* before_last_log); +void debug_dump(std::string filename, std::string s, bool to_append, bool add_newline); +template +NUMBER compute_full_prob(testcase *tc, NUMBER *before_last_log=0); + +#endif diff --git a/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java b/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java index 58b8fc591..a80800f82 100644 --- a/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java +++ b/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java @@ -66,13 +66,13 @@ import java.util.Map; */ public class JNILoglessPairHMM extends LoglessPairHMM { - private static final boolean debug = false; //simulates ifdef + private static final boolean debug = true; //simulates ifdef private static final boolean debug0_1 = false; //simulates ifdef private static final boolean debug1 = false; //simulates ifdef private static final boolean debug2 = false; private static final boolean debug3 = false; private int numComputeLikelihoodCalls = 0; - + //Used to copy references to byteArrays to JNI from reads protected class JNIReadDataHolderClass { @@ -138,6 +138,7 @@ public class JNILoglessPairHMM extends LoglessPairHMM { jniInitialize(readMaxLength, haplotypeMaxLength); } + //Real compute kernel private native void jniComputeLikelihoods(int numReads, int numHaplotypes, JNIReadDataHolderClass[] readDataArray, JNIHaplotypeDataHolderClass[] haplotypeDataArray, @@ -160,6 +161,7 @@ public class JNILoglessPairHMM extends LoglessPairHMM { } int readListSize = reads.size(); int alleleHaplotypeMapSize = alleleHaplotypeMap.size(); + int numTestcases = readListSize*alleleHaplotypeMapSize; if(debug0_1) System.out.println("Java numReads "+readListSize+" numHaplotypes "+alleleHaplotypeMapSize); JNIReadDataHolderClass[] readDataArray = new JNIReadDataHolderClass[readListSize]; @@ -275,6 +277,7 @@ public class JNILoglessPairHMM extends LoglessPairHMM { return likelihoodMap; } + /** * {@inheritDoc} */ From 7180c392af944a2bc47d842e4c9029eb9b7612fb Mon Sep 17 00:00:00 2001 From: Karthik Gururaj Date: Mon, 20 Jan 2014 08:03:42 -0800 Subject: [PATCH 02/10] 1. Integrated Mohammad's SSE4.2 code, Mustafa's bug fix and code to fix the SSE compilation warning. 2. Added code to dynamically select between AVX, SSE4.2 and normal C++ (in that order) 3. Created multiple files to compile with different compilation flags: avx_function_prototypes.cc is compiled with -xAVX while sse_function_instantiations.cc is compiled with -xSSE4.2 flag. 4. Added jniClose() and support in Java (HaplotypeCaller, PairHMMLikelihoodCalculationEngine) to call this function at the end of the program. 5. Removed debug code, kept assertions and profiling in C++ 6. Disabled OpenMP for now. --- PairHMM_JNI/.gitignore | 3 + PairHMM_JNI/Makefile | 6 +- PairHMM_JNI/avx_function_instantiations.cc | 6 + PairHMM_JNI/avx_function_prototypes.h | 19 - PairHMM_JNI/define-double.h | 54 +-- PairHMM_JNI/define-float.h | 4 +- PairHMM_JNI/define-sse-double.h | 128 +++++ PairHMM_JNI/define-sse-float.h | 127 +++++ PairHMM_JNI/headers.h | 1 + PairHMM_JNI/hmm_mask.cc | 451 ------------------ PairHMM_JNI/libJNILoglessPairHMM.so | Bin 96945 -> 0 bytes ...e_sting_utils_pairhmm_JNILoglessPairHMM.cc | 104 ++-- ...te_sting_utils_pairhmm_JNILoglessPairHMM.h | 8 + PairHMM_JNI/pairhmm-1-base.cc | 82 +--- PairHMM_JNI/pairhmm-template-kernel.cc | 161 ++++--- PairHMM_JNI/pairhmm-template-main.cc | 74 ++- PairHMM_JNI/run.sh | 7 +- PairHMM_JNI/shift_template.c | 38 +- PairHMM_JNI/sse_function_instantiations.cc | 18 + PairHMM_JNI/template.h | 47 +- PairHMM_JNI/utils.cc | 35 +- PairHMM_JNI/utils.h | 19 +- PairHMM_JNI/vector_defs.h | 26 + PairHMM_JNI/vector_function_prototypes.h | 19 + .../haplotypecaller/HaplotypeCaller.java | 2 +- .../LikelihoodCalculationEngine.java | 2 + .../PairHMMLikelihoodCalculationEngine.java | 2 + .../utils/pairhmm/JNILoglessPairHMM.java | 38 +- .../sting/utils/pairhmm/PairHMM.java | 3 + 29 files changed, 708 insertions(+), 776 deletions(-) delete mode 100644 PairHMM_JNI/avx_function_prototypes.h create mode 100644 PairHMM_JNI/define-sse-double.h create mode 100644 PairHMM_JNI/define-sse-float.h delete mode 100644 PairHMM_JNI/hmm_mask.cc delete mode 100755 PairHMM_JNI/libJNILoglessPairHMM.so create mode 100644 PairHMM_JNI/sse_function_instantiations.cc create mode 100644 PairHMM_JNI/vector_defs.h create mode 100644 PairHMM_JNI/vector_function_prototypes.h diff --git a/PairHMM_JNI/.gitignore b/PairHMM_JNI/.gitignore index 9722ad970..a68330124 100644 --- a/PairHMM_JNI/.gitignore +++ b/PairHMM_JNI/.gitignore @@ -8,3 +8,6 @@ pairhmm-template-main *.swp checker reformat +subdir_checkout.sh +avx/ +sse/ diff --git a/PairHMM_JNI/Makefile b/PairHMM_JNI/Makefile index 62dc3b0f6..b86958a6c 100644 --- a/PairHMM_JNI/Makefile +++ b/PairHMM_JNI/Makefile @@ -1,4 +1,4 @@ -OMPCFLAGS=-fopenmp +#OMPCFLAGS=-fopenmp #OMPLDFLAGS=-lgomp #CFLAGS=-O2 -std=c++11 -W -Wall -march=corei7-avx -Wa,-q -pedantic $(OMPCFLAGS) -Wno-unknown-pragmas @@ -20,7 +20,7 @@ DEPDIR=.deps DF=$(DEPDIR)/$(*).d #Common across libJNI and sandbox -COMMON_SOURCES=utils.cc avx_function_instantiations.cc baseline.cc +COMMON_SOURCES=utils.cc avx_function_instantiations.cc baseline.cc sse_function_instantiations.cc #Part of libJNI LIBSOURCES=org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc $(COMMON_SOURCES) SOURCES=$(LIBSOURCES) pairhmm-template-main.cc pairhmm-1-base.cc @@ -33,7 +33,7 @@ NO_VECTOR_SOURCES=org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc pa #Use -xAVX for these files AVX_SOURCES=avx_function_instantiations.cc #Use -xSSE4.2 for these files -SSE_SOURCES= +SSE_SOURCES=sse_function_instantiations.cc NO_VECTOR_OBJECTS=$(NO_VECTOR_SOURCES:.cc=.o) AVX_OBJECTS=$(AVX_SOURCES:.cc=.o) diff --git a/PairHMM_JNI/avx_function_instantiations.cc b/PairHMM_JNI/avx_function_instantiations.cc index c29370561..b236ddc8f 100644 --- a/PairHMM_JNI/avx_function_instantiations.cc +++ b/PairHMM_JNI/avx_function_instantiations.cc @@ -1,5 +1,11 @@ #include "template.h" +#undef SIMD_TYPE +#undef SIMD_TYPE_SSE + +#define SIMD_TYPE avx +#define SIMD_TYPE_AVX + #include "define-float.h" #include "shift_template.c" #include "pairhmm-template-kernel.cc" diff --git a/PairHMM_JNI/avx_function_prototypes.h b/PairHMM_JNI/avx_function_prototypes.h deleted file mode 100644 index 1836a1c37..000000000 --- a/PairHMM_JNI/avx_function_prototypes.h +++ /dev/null @@ -1,19 +0,0 @@ -void GEN_INTRINSIC(_vector_shift, PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn, MAIN_TYPE &shiftOut); -void GEN_INTRINSIC(_vector_shift_last, PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn); -void GEN_INTRINSIC(precompute_masks_, PRECISION)(const testcase& tc, int COLS, int numMaskVecs, MASK_TYPE (*maskArr)[NUM_DISTINCT_CHARS]); -void GEN_INTRINSIC(init_masks_for_row_, PRECISION)(const testcase& tc, char* rsArr, MASK_TYPE* lastMaskShiftOut, int beginRowIndex, int numRowsToProcess); -void GEN_INTRINSIC(update_masks_for_cols_, PRECISION)(int maskIndex, MASK_VEC& currMaskVecLow, MASK_VEC& currMaskVecHigh, MASK_TYPE (*maskArr) [NUM_DISTINCT_CHARS], char* rsArr, MASK_TYPE* lastMaskShiftOut, MASK_TYPE maskBitCnt); -void GEN_INTRINSIC(computeDistVec, PRECISION) (MASK_VEC& currMaskVecLow, MASK_VEC& currMaskVecHigh, _256_TYPE& distm, _256_TYPE& _1_distm, _256_TYPE& distmChosen); -template void GEN_INTRINSIC(initializeVectors, PRECISION)(int ROWS, int COLS, NUMBER* shiftOutM, NUMBER *shiftOutX, NUMBER *shiftOutY, Context ctx, testcase *tc, _256_TYPE *p_MM, _256_TYPE *p_GAPM, _256_TYPE *p_MX, _256_TYPE *p_XX, _256_TYPE *p_MY, _256_TYPE *p_YY, _256_TYPE *distm1D); -template void GEN_INTRINSIC(stripINITIALIZATION, PRECISION)( - int stripIdx, Context ctx, testcase *tc, _256_TYPE &pGAPM, _256_TYPE &pMM, _256_TYPE &pMX, _256_TYPE &pXX, _256_TYPE &pMY, _256_TYPE &pYY, - _256_TYPE &rs, UNION_TYPE &rsN, _256_TYPE &distm, _256_TYPE &_1_distm, _256_TYPE *distm1D, _256_TYPE N_packed256, _256_TYPE *p_MM , _256_TYPE *p_GAPM , - _256_TYPE *p_MX, _256_TYPE *p_XX , _256_TYPE *p_MY, _256_TYPE *p_YY, UNION_TYPE &M_t_2, UNION_TYPE &X_t_2, UNION_TYPE &M_t_1, UNION_TYPE &X_t_1, - UNION_TYPE &Y_t_2, UNION_TYPE &Y_t_1, UNION_TYPE &M_t_1_y, NUMBER* shiftOutX, NUMBER* shiftOutM); -_256_TYPE GEN_INTRINSIC(computeDISTM, PRECISION)(int d, int COLS, testcase * tc, HAP_TYPE &hap, _256_TYPE rs, UNION_TYPE rsN, _256_TYPE N_packed256, - _256_TYPE distm, _256_TYPE _1_distm); -void GEN_INTRINSIC(computeMXY, PRECISION)(UNION_TYPE &M_t, UNION_TYPE &X_t, UNION_TYPE &Y_t, UNION_TYPE &M_t_y, - UNION_TYPE M_t_2, UNION_TYPE X_t_2, UNION_TYPE Y_t_2, UNION_TYPE M_t_1, UNION_TYPE X_t_1, UNION_TYPE M_t_1_y, UNION_TYPE Y_t_1, - _256_TYPE pMM, _256_TYPE pGAPM, _256_TYPE pMX, _256_TYPE pXX, _256_TYPE pMY, _256_TYPE pYY, _256_TYPE distmSel); -template NUMBER GEN_INTRINSIC(compute_full_prob_avx, PRECISION) (testcase *tc, NUMBER *before_last_log = NULL); - diff --git a/PairHMM_JNI/define-double.h b/PairHMM_JNI/define-double.h index 5d31741d6..79c6b323f 100644 --- a/PairHMM_JNI/define-double.h +++ b/PairHMM_JNI/define-double.h @@ -18,34 +18,34 @@ #undef MASK_TYPE #undef MASK_ALL_ONES - #undef SET_VEC_ZERO - #undef VEC_OR - #undef VEC_ADD - #undef VEC_SUB - #undef VEC_MUL - #undef VEC_DIV - #undef VEC_BLEND - #undef VEC_BLENDV - #undef VEC_CAST_256_128 - #undef VEC_EXTRACT_128 - #undef VEC_EXTRACT_UNIT - #undef VEC_SET1_VAL128 - #undef VEC_MOVE - #undef VEC_CAST_128_256 - #undef VEC_INSERT_VAL - #undef VEC_CVT_128_256 - #undef VEC_SET1_VAL - #undef VEC_POPCVT_CHAR - #undef VEC_LDPOPCVT_CHAR - #undef VEC_CMP_EQ - #undef VEC_SET_LSE - #undef SHIFT_HAP - #undef print256b + #undef SET_VEC_ZERO(__vec) + #undef VEC_OR(__v1, __v2) + #undef VEC_ADD(__v1, __v2) + #undef VEC_SUB(__v1, __v2) + #undef VEC_MUL(__v1, __v2) + #undef VEC_DIV(__v1, __v2) + #undef VEC_BLEND(__v1, __v2, __mask) + #undef VEC_BLENDV(__v1, __v2, __maskV) + #undef VEC_CAST_256_128(__v1) + #undef VEC_EXTRACT_128(__v1, __im) + #undef VEC_EXTRACT_UNIT(__v1, __im) + #undef VEC_SET1_VAL128(__val) + #undef VEC_MOVE(__v1, __val) + #undef VEC_CAST_128_256(__v1) + #undef VEC_INSERT_VAL(__v1, __val, __pos) + #undef VEC_CVT_128_256(__v1) + #undef VEC_SET1_VAL(__val) + #undef VEC_POPCVT_CHAR(__ch) + #undef VEC_LDPOPCVT_CHAR(__addr) + #undef VEC_CMP_EQ(__v1, __v2) + #undef VEC_SET_LSE(__val) + #undef SHIFT_HAP(__v1, __val) + #undef print256b(__v1) #undef MASK_VEC - #undef VEC_SSE_TO_AVX - #undef VEC_SHIFT_LEFT_1BIT + #undef VEC_SSE_TO_AVX(__vsLow, __vsHigh, __vdst) + #undef VEC_SHIFT_LEFT_1BIT(__vs) #undef MASK_ALL_ONES - #undef COMPARE_VECS + #undef COMPARE_VECS(__v1, __v2) #undef _256_INT_TYPE #endif @@ -83,7 +83,7 @@ #define VEC_MUL(__v1, __v2) \ _mm256_mul_pd(__v1, __v2) -#define VEC_DIV(__v1, __v2) \ +#define VEC_DIV(__v1, __v2) \ _mm256_div_pd(__v1, __v2) #define VEC_BLEND(__v1, __v2, __mask) \ diff --git a/PairHMM_JNI/define-float.h b/PairHMM_JNI/define-float.h index 0e2822b2d..25ebd1489 100644 --- a/PairHMM_JNI/define-float.h +++ b/PairHMM_JNI/define-float.h @@ -84,7 +84,7 @@ #define VEC_MUL(__v1, __v2) \ _mm256_mul_ps(__v1, __v2) -#define VEC_DIV(__v1, __v2) \ +#define VEC_DIV(__v1, __v2) \ _mm256_div_ps(__v1, __v2) #define VEC_BLEND(__v1, __v2, __mask) \ @@ -133,7 +133,7 @@ _mm256_set_ps(zero, zero, zero, zero, zero, zero, zero, __val); #define SHIFT_HAP(__v1, __val) \ - _vector_shift_lasts(__v1, __val.f); + _vector_shift_lastavxs(__v1, __val.f); #define print256b(__v1) \ print256bFP(__v1) diff --git a/PairHMM_JNI/define-sse-double.h b/PairHMM_JNI/define-sse-double.h new file mode 100644 index 000000000..e48325ba9 --- /dev/null +++ b/PairHMM_JNI/define-sse-double.h @@ -0,0 +1,128 @@ +#ifdef PRECISION + #undef PRECISION + #undef MAIN_TYPE + #undef MAIN_TYPE_SIZE + #undef UNION_TYPE + #undef IF_128 + #undef IF_MAIN_TYPE + #undef SHIFT_CONST1 + #undef SHIFT_CONST2 + #undef SHIFT_CONST3 + #undef _128_TYPE + #undef _256_TYPE + #undef AVX_LENGTH + #undef MAVX_COUNT + #undef HAP_TYPE + #undef MASK_TYPE + #undef MASK_ALL_ONES + + #undef VEC_EXTRACT_UNIT(__v1, __im) + #undef VEC_INSERT_UNIT(__v1,__ins,__im) + #undef SET_VEC_ZERO(__vec) + #undef VEC_OR(__v1, __v2) + #undef VEC_ADD(__v1, __v2) + #undef VEC_SUB(__v1, __v2) + #undef VEC_MUL(__v1, __v2) + #undef VEC_DIV(__v1, __v2) + #undef VEC_BLEND(__v1, __v2, __mask) + #undef VEC_BLENDV(__v1, __v2, __maskV) + #undef VEC_CAST_256_128(__v1) + #undef VEC_EXTRACT_128(__v1, __im) + #undef VEC_EXTRACT_UNIT(__v1, __im) + #undef VEC_SET1_VAL128(__val) + #undef VEC_MOVE(__v1, __val) + #undef VEC_CAST_128_256(__v1) + #undef VEC_INSERT_VAL(__v1, __val, __pos) + #undef VEC_CVT_128_256(__v1) + #undef VEC_SET1_VAL(__val) + #undef VEC_POPCVT_CHAR(__ch) + #undef VEC_LDPOPCVT_CHAR(__addr) + #undef VEC_CMP_EQ(__v1, __v2) + #undef VEC_SET_LSE(__val) + #undef SHIFT_HAP(__v1, __val) + #undef print256b(__v1) + #undef MASK_VEC + #undef VEC_SSE_TO_AVX(__vsLow, __vsHigh, __vdst) + #undef VEC_SHIFT_LEFT_1BIT(__vs) + #undef MASK_ALL_ONES + #undef COMPARE_VECS(__v1, __v2) + #undef _256_INT_TYPE + +#endif + +#define SSE +#define PRECISION d + +#define MAIN_TYPE double +#define MAIN_TYPE_SIZE 64 +#define UNION_TYPE mix_D128 +#define IF_128 IF_128d +#define IF_MAIN_TYPE IF_64 +#define SHIFT_CONST1 1 +#define SHIFT_CONST2 8 +#define SHIFT_CONST3 0 +#define _128_TYPE __m128d +#define _256_TYPE __m128d +#define _256_INT_TYPE __m128i +#define AVX_LENGTH 2 +#define MAVX_COUNT (MROWS+3)/AVX_LENGTH +#define HAP_TYPE __m128i +#define MASK_TYPE uint64_t +#define MASK_ALL_ONES 0xFFFFFFFFFFFFFFFFL +#define MASK_VEC MaskVec_D128 + +#define VEC_EXTRACT_UNIT(__v1, __im) \ + _mm_extract_epi64(__v1, __im) + +#define VEC_INSERT_UNIT(__v1,__ins,__im) \ + _mm_insert_epi64(__v1,__ins,__im) + +#define VEC_OR(__v1, __v2) \ + _mm_or_pd(__v1, __v2) + +#define VEC_ADD(__v1, __v2) \ + _mm_add_pd(__v1, __v2) + +#define VEC_SUB(__v1, __v2) \ + _mm_sub_pd(__v1, __v2) + +#define VEC_MUL(__v1, __v2) \ + _mm_mul_pd(__v1, __v2) + +#define VEC_DIV(__v1, __v2) \ + _mm_div_pd(__v1, __v2) + +#define VEC_CMP_EQ(__v1, __v2) \ + _mm_cmpeq_pd(__v1, __v2) + +#define VEC_BLEND(__v1, __v2, __mask) \ + _mm_blend_pd(__v1, __v2, __mask) + +#define VEC_BLENDV(__v1, __v2, __maskV) \ + _mm_blendv_pd(__v1, __v2, __maskV) + +#define SHIFT_HAP(__v1, __val) \ + __v1 = _mm_insert_epi32(_mm_slli_si128(__v1, 4), __val.i, 0) + +#define VEC_CVT_128_256(__v1) \ + _mm_cvtepi32_pd(__v1) + +#define VEC_SET1_VAL(__val) \ + _mm_set1_pd(__val) + +#define VEC_POPCVT_CHAR(__ch) \ + _mm_cvtepi32_pd(_mm_set1_epi32(__ch)) + +#define VEC_SET_LSE(__val) \ + _mm_set_pd(zero, __val); + +#define VEC_LDPOPCVT_CHAR(__addr) \ + _mm_cvtepi32_pd(_mm_loadu_si128((__m128i const *)__addr)) + +#define VEC_SSE_TO_AVX(__vsLow, __vsHigh, __vdst) \ + __vdst = _mm_castsi128_pd(_mm_set_epi64(__vsHigh, __vsLow)) + +#define VEC_SHIFT_LEFT_1BIT(__vs) \ + __vs = _mm_slli_si64(__vs, 1) + + diff --git a/PairHMM_JNI/define-sse-float.h b/PairHMM_JNI/define-sse-float.h new file mode 100644 index 000000000..f5758c74a --- /dev/null +++ b/PairHMM_JNI/define-sse-float.h @@ -0,0 +1,127 @@ +#ifdef PRECISION + #undef PRECISION + #undef MAIN_TYPE + #undef MAIN_TYPE_SIZE + #undef UNION_TYPE + #undef IF_128 + #undef IF_MAIN_TYPE + #undef SHIFT_CONST1 + #undef SHIFT_CONST2 + #undef SHIFT_CONST3 + #undef _128_TYPE + #undef _256_TYPE + #undef AVX_LENGTH + #undef MAVX_COUNT + #undef HAP_TYPE + #undef MASK_TYPE + #undef MASK_ALL_ONES + + #undef VEC_EXTRACT_UNIT(__v1, __im) + #undef VEC_INSERT_UNIT(__v1,__ins,__im) + #undef SET_VEC_ZERO(__vec) + #undef VEC_OR(__v1, __v2) + #undef VEC_ADD(__v1, __v2) + #undef VEC_SUB(__v1, __v2) + #undef VEC_MUL(__v1, __v2) + #undef VEC_DIV(__v1, __v2) + #undef VEC_BLEND(__v1, __v2, __mask) + #undef VEC_BLENDV(__v1, __v2, __maskV) + #undef VEC_CAST_256_128(__v1) + #undef VEC_EXTRACT_128(__v1, __im) + #undef VEC_EXTRACT_UNIT(__v1, __im) + #undef VEC_SET1_VAL128(__val) + #undef VEC_MOVE(__v1, __val) + #undef VEC_CAST_128_256(__v1) + #undef VEC_INSERT_VAL(__v1, __val, __pos) + #undef VEC_CVT_128_256(__v1) + #undef VEC_SET1_VAL(__val) + #undef VEC_POPCVT_CHAR(__ch) + #undef VEC_LDPOPCVT_CHAR(__addr) + #undef VEC_CMP_EQ(__v1, __v2) + #undef VEC_SET_LSE(__val) + #undef SHIFT_HAP(__v1, __val) + #undef print256b(__v1) + #undef MASK_VEC + #undef VEC_SSE_TO_AVX(__vsLow, __vsHigh, __vdst) + #undef VEC_SHIFT_LEFT_1BIT(__vs) + #undef MASK_ALL_ONES + #undef COMPARE_VECS(__v1, __v2) + #undef _256_INT_TYPE + +#endif + +#define SSE +#define PRECISION s + +#define MAIN_TYPE float +#define MAIN_TYPE_SIZE 32 +#define UNION_TYPE mix_F128 +#define IF_128 IF_128f +#define IF_MAIN_TYPE IF_32 +#define SHIFT_CONST1 3 +#define SHIFT_CONST2 4 +#define SHIFT_CONST3 0 +#define _128_TYPE __m128 +#define _256_TYPE __m128 +#define _256_INT_TYPE __m128i +#define AVX_LENGTH 4 +#define MAVX_COUNT (MROWS+3)/AVX_LENGTH +#define HAP_TYPE UNION_TYPE +#define MASK_TYPE uint32_t +#define MASK_ALL_ONES 0xFFFFFFFF +#define MASK_VEC MaskVec_F128 + +#define VEC_EXTRACT_UNIT(__v1, __im) \ + _mm_extract_epi32(__v1, __im) + +#define VEC_INSERT_UNIT(__v1,__ins,__im) \ + _mm_insert_epi32(__v1,__ins,__im) + +#define VEC_OR(__v1, __v2) \ + _mm_or_ps(__v1, __v2) + +#define VEC_ADD(__v1, __v2) \ + _mm_add_ps(__v1, __v2) + +#define VEC_SUB(__v1, __v2) \ + _mm_sub_ps(__v1, __v2) + +#define VEC_MUL(__v1, __v2) \ + _mm_mul_ps(__v1, __v2) + +#define VEC_DIV(__v1, __v2) \ + _mm_div_ps(__v1, __v2) + +#define VEC_CMP_EQ(__v1, __v2) \ + _mm_cmpeq_ps(__v1, __v2) + +#define VEC_BLEND(__v1, __v2, __mask) \ + _mm_blend_ps(__v1, __v2, __mask) + +#define VEC_BLENDV(__v1, __v2, __maskV) \ + _mm_blendv_ps(__v1, __v2, __maskV) + +#define SHIFT_HAP(__v1, __val) \ + _vector_shift_lastsses(__v1, __val.f) + +#define VEC_CVT_128_256(__v1) \ + _mm_cvtepi32_ps(__v1.i) + +#define VEC_SET1_VAL(__val) \ + _mm_set1_ps(__val) + +#define VEC_POPCVT_CHAR(__ch) \ + _mm_cvtepi32_ps(_mm_set1_epi32(__ch)) + +#define VEC_SET_LSE(__val) \ + _mm_set_ps(zero, zero, zero, __val); + +#define VEC_LDPOPCVT_CHAR(__addr) \ + _mm_cvtepi32_ps(_mm_loadu_si128((__m128i const *)__addr)) + +#define VEC_SSE_TO_AVX(__vsLow, __vsHigh, __vdst) \ + __vdst = _mm_cvtpi32x2_ps(__vsLow, __vsHigh) + +#define VEC_SHIFT_LEFT_1BIT(__vs) \ + __vs = _mm_slli_pi32(__vs, 1) + diff --git a/PairHMM_JNI/headers.h b/PairHMM_JNI/headers.h index 42485fc51..f502ff5ce 100644 --- a/PairHMM_JNI/headers.h +++ b/PairHMM_JNI/headers.h @@ -17,6 +17,7 @@ #include #include #include +#include #include #include #include diff --git a/PairHMM_JNI/hmm_mask.cc b/PairHMM_JNI/hmm_mask.cc deleted file mode 100644 index c335a0160..000000000 --- a/PairHMM_JNI/hmm_mask.cc +++ /dev/null @@ -1,451 +0,0 @@ -#include -#include -#include -#include - - -// /usr/intel/pkgs/icc/13.0.0e/bin/icc -o ed -O3 ed.cpp -xAVX -openmp -openmp-link static - - -#include - -#include -#include -#include -#include -#include -#include - - -#include -#include -#include -#include -#include - - -#include -#include -#include -#include -#include -#include -#include "template.h" - -using namespace std ; - -#define CHECK_MASK_CORRECTNESS - -template -string getBinaryStr (T val, int numBitsToWrite) { - - ostringstream oss ; - uint64_t mask = ((T) 0x1) << (numBitsToWrite-1) ; - for (int i=numBitsToWrite-1; i >= 0; --i) { - oss << ((val & mask) >> i) ; - mask >>= 1 ; - } - return oss.str() ; -} - -int normalize(char c) -{ - return ((int) (c - 33)); -} - - -uint8_t ConvertChar::conversionTable[255] ; - -int read_testcase(testcase *tc, FILE* ifp) -{ - char *q, *i, *d, *c, *line = NULL; - int _q, _i, _d, _c; - int x, size = 0; - ssize_t read; - - read = getline(&line, (size_t *) &size, ifp == 0 ? stdin : ifp); - if (read == -1) - return -1; - - - tc->hap = (char *) malloc(size); - tc->rs = (char *) malloc(size); - q = (char *) malloc(size); - i = (char *) malloc(size); - d = (char *) malloc(size); - c = (char *) malloc(size); - - if (sscanf(line, "%s %s %s %s %s %s\n", tc->hap, tc->rs, q, i, d, c) != 6) - return -1; - - tc->haplen = strlen(tc->hap); - tc->rslen = strlen(tc->rs); - assert(tc->rslen < MROWS); - tc->ihap = (int *) malloc(tc->haplen*sizeof(int)); - tc->irs = (int *) malloc(tc->rslen*sizeof(int)); - - //tc->q = (int *) malloc(sizeof(int) * tc->rslen); - //tc->i = (int *) malloc(sizeof(int) * tc->rslen); - //tc->d = (int *) malloc(sizeof(int) * tc->rslen); - //tc->c = (int *) malloc(sizeof(int) * tc->rslen); - - for (x = 0; x < tc->rslen; x++) - { - _q = normalize(q[x]); - _i = normalize(i[x]); - _d = normalize(d[x]); - _c = normalize(c[x]); - tc->q[x] = (_q < 6) ? 6 : _q; - tc->i[x] = _i; - tc->d[x] = _d; - tc->c[x] = _c; - tc->irs[x] = tc->rs[x]; - } - for (x = 0; x < tc->haplen; x++) - tc->ihap[x] = tc->hap[x]; - - - free(q); - free(i); - free(d); - free(c); - free(line); - - return 0; -} - - - - -#define ALIGN __attribute__ ((aligned(32))) - - -typedef union ALIGN { - //__m256i vi; - __m128 vf ; - __m128i vi ; - __m128d vd; - - uint8_t b[16]; - //uint16_t hw[8] ; - uint32_t w[4] ; - uint64_t dw[2] ; - - float f[4] ; - //double d[2] ; - -} v128 ; - -typedef union ALIGN { - __m256i vi; - __m256 vf ; - __m256d vd; - //__m128i vi128[2] ; - - uint8_t b[32]; - //uint16_t hw[16] ; - uint32_t w[8] ; - uint64_t dw[4] ; - - float f[8] ; - double d[4] ; - -} v256 ; - - -#define NUM_DISTINCT_CHARS 5 -#define AMBIG_CHAR 4 - -#define VEC_ENTRY_CNT 8 -#define VEC_LEN 256 -#define VTYPE vf -#define VTYPEI vi -#define VENTRY f -#define VENTRYI w -#define MTYPE uint32_t -#define MASK_ALL_ONES 0xFFFFFFFF - - -#define VECTOR v256 -#define VECTOR_SSE v128 - -#define SET_VEC_ZERO(__vec) \ - __vec= _mm256_setzero_ps() - -#define SET_VEC_ONES(__vec) \ - __vec = _mm256_set1_epi32(0xFFFFFFFF) - -#define VEC_OR(__v1, __v2) \ - _mm256_or_ps(__v1, __v2) - -#define VEC_ADD(__v1, __v2) \ - _mm256_add_ps(__v1, __v2) - -#define VEC_MUL(__v1, __v2) \ - _mm256_mul_ps(__v1, __v2) - -#define VEC_BLENDV(__v1, __v2, __maskV) \ - _mm256_blendv_ps(__v1, __v2, __maskV) - -#define VEC_SSE_TO_AVX(__vsLow, __vsHigh, __vdst) \ - __vdst = _mm256_castps128_ps256(__vsLow) ; \ - __vdst = _mm256_insertf128_ps(__vdst, __vsHigh, 1) ; - -#define VECS_SHIFT_LEFT_1BIT(__vs) \ - __vs = _mm_slli_epi32(__vs, 1) - - -#define VECS_SHIFT_RIGHT_WHOLE(__vs, __cnt) { \ - uint64_t __mask = ; \ - uint64_t __shiftWord = ((((uint64_t) 0x1) << __cnt) - 1 ) & __vs.dw[1] ; \ - __shiftWord <<= (64-cnt) ; \ - __vs = _mm_slri_epi64(__vs, __cnt) ; \ - __vs.dw[0] |= __shiftWord ; \ - } - - -#define GET_MASK_WORD_OLD(__mask, __lastShiftOut, __shiftBy, __maskBitCnt){ \ - MTYPE __bitMask = (((MTYPE)0x1) << __shiftBy) - 1 ; \ - MTYPE __nextShiftOut = (__mask & __bitMask) << (__maskBitCnt - __shiftBy) ; \ - __mask >>= __shiftBy ; \ - __mask |= __lastShiftOut ; \ - __lastShiftOut = __nextShiftOut ; \ - } - - -#define SET_MASK_WORD(__dstMask, __srcMask, __lastShiftOut, __shiftBy, __maskBitCnt){ \ - MTYPE __bitMask = (((MTYPE)0x1) << __shiftBy) - 1 ; \ - MTYPE __nextShiftOut = (__srcMask & __bitMask) << (__maskBitCnt - __shiftBy) ; \ - __dstMask = (__srcMask >> __shiftBy) | __lastShiftOut ; \ - __lastShiftOut = __nextShiftOut ; \ -} - - -void precompute_masks_avx(const testcase& tc, int COLS, int numMaskVecs, - MTYPE (*maskArr)[NUM_DISTINCT_CHARS]) { - - const int maskBitCnt = VEC_LEN / VEC_ENTRY_CNT ; - - for (int vi=0; vi < numMaskVecs; ++vi) { - for (int rs=0; rs < NUM_DISTINCT_CHARS; ++rs) { - maskArr[vi][rs] = 0 ; - } - maskArr[vi][AMBIG_CHAR] = MASK_ALL_ONES ; - } - - for (int col=1; col < COLS; ++col) { - int mIndex = (col-1) / maskBitCnt ; - int mOffset = (col-1) % maskBitCnt ; - MTYPE bitMask = ((MTYPE)0x1) << (maskBitCnt-1-mOffset) ; - - char hapChar = tc.hap[col-1] ; - - if (hapChar == AMBIG_CHAR) { - maskArr[mIndex][0] |= bitMask ; - maskArr[mIndex][1] |= bitMask ; - maskArr[mIndex][2] |= bitMask ; - maskArr[mIndex][3] |= bitMask ; - } - - //cout << hapChar << " " << mIndex << " " << getBinaryStr(bitMask, 32) - // << endl ; - //cout << getBinaryStr(maskArr[0][hapChar],32) << endl ; - //exit(0) ; - - maskArr[mIndex][ConvertChar::get(hapChar)] |= bitMask ; - - - // bit corresponding to col 1 will be the MSB of the mask 0 - // bit corresponding to col 2 will be the MSB-1 of the mask 0 - // ... - // bit corresponding to col 32 will be the LSB of the mask 0 - // bit corresponding to col 33 will be the MSB of the mask 1 - // ... - } - -} - - -void test_mask_computations (testcase& tc, int tcID, bool printDebug=false) { - - int ROWS = tc.rslen + 1 ; - int COLS = tc.haplen + 1 ; - - // only for testing - VECTOR mismatchData, matchData ; - //SET_VEC_ZERO(mismatchData.VTYPE) ; - //SET_VEC_ONES(matchData.VTYPEI) ; - for (int ei=0; ei < VEC_ENTRY_CNT; ++ei) { - matchData.VENTRY[ei] = 1.0 ; - mismatchData.VENTRY[ei] = 0.0 ; - } - - const int maskBitCnt = VEC_LEN / VEC_ENTRY_CNT ; - const int numMaskVecs = (COLS+ROWS+maskBitCnt-1)/maskBitCnt ; // ceil function - - MTYPE maskArr[numMaskVecs][NUM_DISTINCT_CHARS] ; - precompute_masks_avx(tc, COLS, numMaskVecs, maskArr) ; - -#ifdef DEBUG - if (printDebug) { - cout << "The first 32 hap chars are: " ; - for (int i=0; i < 32; ++i) { - cout << tc.hap[i] ; - } - cout << endl ; - - cout << "Masks computed for A, C, T, G, N are: " << endl ; - cout << getBinaryStr(maskArr[0][0], 32) << endl ; - cout << getBinaryStr(maskArr[0][1], 32) << endl ; - cout << getBinaryStr(maskArr[0][2], 32) << endl ; - cout << getBinaryStr(maskArr[0][3], 32) << endl ; - cout << getBinaryStr(maskArr[0][4], 32) << endl ; - } -#endif // #ifdef DEBUG - - int beginRowIndex = 1 ; - while (beginRowIndex < ROWS) { - - int numRowsToProcess = min(VEC_ENTRY_CNT, ROWS - beginRowIndex) ; - - char rsArr[VEC_ENTRY_CNT] ; - for (int ri=0; ri < numRowsToProcess; ++ri) { - rsArr[ri] = ConvertChar::get(tc.rs[ri+beginRowIndex-1]) ; - } - - // Since there are no shift intrinsics in AVX, keep the masks in 2 SSE vectors - VECTOR_SSE currMaskVecLow ; // corresponding to entries 0-3 - VECTOR_SSE currMaskVecHigh ; // corresponding to entries 4-7 - - MTYPE lastMaskShiftOut[VEC_ENTRY_CNT] ; - for (int ei=0; ei < VEC_ENTRY_CNT; ++ei) - lastMaskShiftOut[ei] = 0 ; - - int col = 1 ; - int diag = 1 ; - for (int maskIndex=0; maskIndex < numMaskVecs; ++maskIndex) { - // set up the mask vectors for the next maskBitCnt columns - - // For AVX, maskBitCnt = 32 (so, the operation below is amortized over 32 cols) - for (int ei=0; ei < VEC_ENTRY_CNT/2; ++ei) { - SET_MASK_WORD(currMaskVecLow.VENTRYI[ei], maskArr[maskIndex][rsArr[ei]], - lastMaskShiftOut[ei], ei, maskBitCnt) ; - - int ei2 = ei + VEC_ENTRY_CNT/2 ; // the second entry index - SET_MASK_WORD(currMaskVecHigh.VENTRYI[ei], maskArr[maskIndex][rsArr[ei2]], - lastMaskShiftOut[ei2], ei2, maskBitCnt) ; - } - -#ifdef DEBUG - if (printDebug && maskIndex == 0) { - cout << "The masks for entry 1: " << endl - << getBinaryStr(maskArr[0][rsArr[1]], 32) << endl - << getBinaryStr(currMaskVecLow.VENTRYI[1], 32) << endl ; - - } -#endif // #ifdef DEBUG - - // iterate over mask bit indices and columns - for (int mbi=0; mbi < maskBitCnt && diag < COLS + ROWS -2; ++mbi, ++diag) { - - VECTOR maskV ; - VEC_SSE_TO_AVX(currMaskVecLow.VTYPE, currMaskVecHigh.VTYPE, maskV.VTYPE) ; - - VECTOR testData ; - testData.VTYPE = VEC_BLENDV(mismatchData.VTYPE, matchData.VTYPE, - maskV.VTYPE) ; - - VECS_SHIFT_LEFT_1BIT(currMaskVecLow.VTYPEI) ; - VECS_SHIFT_LEFT_1BIT(currMaskVecHigh.VTYPEI) ; - -#ifdef DEBUG - if (printDebug && maskIndex == 0) { - cout << "The mask for entry 1, mbi=" << mbi << ": " - << getBinaryStr(maskV.VENTRYI[1], 32) << endl ; - - } -#endif // #ifdef DEBUG - -#ifdef CHECK_MASK_CORRECTNESS - - int firstRowIndex = (diag < COLS) ? 0 : (diag - COLS) ; - int lastRowIndex = min(col-1, numRowsToProcess-1) ; - - for (int ri=firstRowIndex; ri <= lastRowIndex; ++ri) { - int currRow = beginRowIndex + ri ; - int currCol = col - ri + firstRowIndex ; - - char hapChar = tc.hap[currCol-1] ; - char rsChar = tc.rs[currRow-1] ; - - bool match = (hapChar == rsChar || hapChar == 'N' || rsChar == 'N') ; - - if ((bool) testData.VENTRYI[ri] != match) { - cout << "Error: Incorrect mask for tc " << tcID << ", diag = " << diag - << " (" << currRow << ", " << currCol << ")" << endl ; - - cout << "The chars are: " << hapChar << " and " << rsChar << endl ; - cout << "The selected value is: " << testData.VENTRYI[ri] << endl ; - - exit(0) ; - } - } -#endif // #ifdef CHECK_MASK_CORRECTNESS - - if (diag < COLS) - ++col ; - - } // mbi - } // maskIndex - - beginRowIndex += VEC_ENTRY_CNT ; - } // end of stripe - - //cout << "Finished validating entry " << endl ; -} - -#ifdef HMM_MASK_MAIN -int main () { - - #define BATCH_SIZE 10000 - - ConvertChar::init() ; - - testcase* tcBatch = new testcase[BATCH_SIZE] ; - - int numBatches = 0 ; - int numRead = 0 ; - - const int DEBUG_TC = -1 ; - - FILE* ifp = stdin ; - - do { - numRead = 0 ; - - while (numRead < BATCH_SIZE) { - if (read_testcase(tcBatch+numRead, ifp) < 0) - break ; - - ++numRead ; - } - - if (numRead == 0) - break ; - - for (int ti=0; ti < numRead; ++ti) { - - int tcID = numBatches * BATCH_SIZE + ti ; - test_mask_computations(tcBatch[ti], tcID, tcID == DEBUG_TC) ; - } - - ++numBatches ; - } while (numRead == BATCH_SIZE) ; - - fclose(ifp) ; - - delete[] tcBatch ; - - return 0 ; -} -#endif diff --git a/PairHMM_JNI/libJNILoglessPairHMM.so b/PairHMM_JNI/libJNILoglessPairHMM.so deleted file mode 100755 index 9b6b8e6930e9764b187f019979a525dd50840252..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 96945 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

cZ-x*bL@^bRF-^(%PSLf5Xx1N{0S zk9PgXO8q&fQq>Zrj6PW5b`3#qSE}lw6utMCpSc}r?E@N+^eA3IFaLB~@7;Kfx+^Vb z=hoS-A=RdQ<7R@TV>$!~T#4kb971wPdKt=}2+Gd(X2v5sw@Qh(S{YqKmXbkQcG~o` z+tcotHeFfv1=rM7{eq^i^0a|xn*Oe)S9=`5YWg<4uSW^3@m#?)!9Lrn#hasjb!#6; zjbyQspy{WT;0BT3O5Twsrc;`kfI6p9^+xbES#YrIN^a;Nmwkt<*mE!JhP+j&+AQRB zxT*~&xC5j}05<{j)qLryepI}C;;Np@1jLLNFNa;#SK=jYS^GbukRkL{FZ{?xv?rsc zT?wucOqJj&!7W|i>{*C3(8*#v6n%|n2ei2fve6QqrFaFwPq>O zrFpjlGh%?gVNKS>DB<8{tKe~_+MuW3a*qi-p1Og^QAWFf{hIG+=) z{?(pVusz|5?fr`v9FBi{3sH@Ok`#@0+w6(E2AJzGQYoR7hmnN1W@Mp;1?>>5{&nyN zX0a#6@S;;Qje#`A(*bQhSHr24I>QpN-*VqS~Rl68FRp0)we?kDUGoJ2)k_2 z9wT3%Vpj=qH67%f9j^F#qc2Nf!AfX@Ec5=;-IQ6$GKG8z=vJGcwn#z}a(t>pZm&{E1}LQ9)HZNP0u!YHAsH8RbU!YT${@)uci2WpHou`&%jKKZ-~K0x-B z$ju%mGMc}J7&UdS>bp^D)nZRyM_?cmfw|D3Nc3Ce1T9w5IByg*!Sx|Y8z5<%6AGG; zJP$NcW!UE$p-00Fl%j7{YT&cq7)ZTBBVQSRf*p;c+joT?2Ua2WL!$zYCtW|1*507h zABk3iYef@ect3;DU!myPSH!rMufYT_d<>4kv<|BweR@NBFe;;_&a#z%+4$0YXe@DV zXg@g8wc;rBl#%>pTKkT4y~vY=F7)QRZZu4~Cp%p~Mcj-{KS6DwtsJ4!s)p8JT=LGe z*9axo>4QCn@NZQOvER622R{7=Y9`w=13RcMd>R%c z@NvL{YP^KvT=6>u1YCd36K^~T91wWrAf^T{gAJv7^F0daecy8}k3$q3UJujJg3<+O zh>M;>J++`T1uZE30gaBXLIHjUpUWEPgG`@3?zMN^4r4Hk7iJM7yH7uMTnEw^`K?yB z5jfFZvLp`OMc_m?84H}{uZ(sA93N)WwTkg!-2MY7<*%qWOKEu0a9q}~x*4OhOz=5}WztZ~m|KzLFHE9;NNEn< z0=OPt4Oy53K8@o}Dveb9QTUc+OlFVH+hB@>1aN6E^LPcqia&{b8)aNa)?g5C3`P}` zzeIe;S~y%QzQ#%z{D6Lp!Kne9@jQx!^&@01+atUtT&WU_HTId_BiMU}4WAWjye0_M zo{eCLTx0CiID`I+VQ{Ua4z^R>(!YZGoTOgZ-BDszjNwOKh(bSo7>N!d5gzZ$M>8I- z>Sy5*b-1|Ao|S-&x4vXWQ;t2uCfe*-4cvCL7#`?*Kw;p()A&rZ`oKHPc<%||kc^CF zvJE1q(GG6Fvs{Va>|s*lr(d8HI&QdS2Xx5Vk0$wen`yv%Bc9O2B3CC(Y^98$l=T?QEny%Z^K{b0 z`|wptXrNC-2@!>{1WOa58YjUG*(rdf2_gK|Ev6>UT_!Za1hN+54S>}Q#y+rsv}7$) zh&MBZD5O@Vpo}twcr#Ooc6$UVgeK~Mg(e#0V+YfKcP*aKL>6SR*~Dvw~T_o8CY1&7?4R{J5vzwZ@IBaU-Pkn_Zm_XJ-yaBMd z8n1%|q+J(DA>PasLKACc3d$&Xh&MBZ(8T>nkzD7ip@UwCdoi@J6bW3_m!N!G?f*i# zLMfMZu04}>s{Jn57;5kRV~3mwGdN>~+-B8311lgoo9(Dff5WWzqnD!Ii#-cvShf&p zIugB!oS-e1w8a)p+COOsorx6QM7#l@zC>LgK?)JIeQ11xYNNJg5{$%nW08VBeHXHRgLP-cMaD)5PHY4ZJT~>?xCr2Tuc%f{Xs-Mg3egb0sn&?HNcbDM=j^A?imF zN?VLz#5_XI#t7vfpd{B}w!k{gaIF}LHdKOmW%cTt5$Vdw2FY`j;D}g5M_ymy`(o4v zzoi%?6$Ex5ZWd!bLcOw@eOfR(DQ3JrV2FkgfoUh`>CR{kYacQrnq-n*eQB1+EmQdP zHBJR^npgsHtylw%X26sS-((iXhZGFUY}pJ9HBX#^5YZikwq~zp%*0e_?R7rq*SEP| zA3SKHp7S`ONQpO*{U_>cA3t~zl+hV~Iq}b7#OQ*LP4{jmY2gnc3RYB}5b-b7gCMTG zF3r15s%`mB#&7y@a~>|WOhIFexu18Yuw_IQjI1sc+8!x!#?6ufgEbjuBeuMTNEDs= zI=fl&K1?-3i;EqTg3)c0^z=4VRr91y3XMHu-0=xb^dqWgrm=}8Nm6^G_eBxrWCjPH zQFN!GPs5zWg&AQNOFjra8qUv5GZzE<4;r^Y9&=F&>9`^VLpD+=JyRNg zWdo9h@uKa_3RyuAM7XhzBcQ&YG229oJ*I!YbkM4`?YqmL+bD+*mDk}rBY z9djf$&I#2u^wSe*uA%Q_xc>Yl?o%Kz)<5AI+DQC|K;!)_Cj1hUzTj5ah`w9ZcdGgu zs=iy%_gLjB`Wy6ybTg_B39H4bi>mM9(k-VYs{W>mM7&kzYGf2*rN@Lcp5b}OL&ucY^*+ptSF(W11$=EQ=&y=@2ci1K%|7f zumv9NDB(SpFt($Fx(-%C*kzJcLkC)vFjt~?5RFX=dkIrm0#7lRbvVru4xv@eI#hSC z5+w|gtk!j)MF}wyy`E@Sbu)JPBX#%^O%UEqjI@%093vK5C5V_%tUU(lA}q!Ns@0M`OlL4Pe=mhpFR%qFiGx5@@h$5^r#?jBl$*2{tERm?vv#CGWQ=z zzCn`rkUZ5a8-;@TN|F;Kxt8Q)N&WfN%HlQTu$=I_eH%+ zBsqcPcuAf{@;j0|5fK)0_a*s9l01Us|CQvGlKkakAYU!X14({Dl6Ok-ZjuK}@~K~f zyhxHS0Upf%O!8Hd{4U9NO7bL0UQP0qlDv^*FUita<}|t;#w7GJ>6o}&Q~V*?e2Lg8 z9ru!~++=b%3)4fR&L@X6heh2pWwYEw@)=3KgXBY!+#<=bB%hSzQ6&EjWY-YvcfxFv zX{|DS%1`l*c*OHJJc8qD{tn7aiW(gFbG$1U!}WI*{asaWQuTdUD$PhfjK!ZDLCfHt zKxU$*tE19a&PdyqIw5LX>O|+Z)XCA?QnPz$`s7qipQ8A5CCjI0&dAVn5VfVFPgU0i z#%RH7Rq)VsERUtU0b4OLWvPcZQ;?LwEIiozRRQ(8PKeS=(JyNmg`BQP&byg2R_1(0 z<~%AMy`6$VzZVJV%!#6m`A{=zc`uQnfr*<$QTG8#%L&hgf@ZIl`@yPy07Imze=D{` z`(owWhi!-IPtcxfFxmrF8Yi3sXTxmJJa^%NOy#tl2mjlx)Yxww_?k>zB2&9Nr@kBp6OYu2K0e;sk_(Na-$MIV!6r8ymc}?Q zgcck2o-pMWY3cbSzbnZa$u3D=Bgq*gb0C4JHVMl8>(f`DIBCO0ofmx9JT@ZX)^Dl3etN$i0(f4lyYEFC+)dvL*R>lDA9pqa;rU z*~N1V)-eO_R^rS-DiBMN&e8S!L035K4xG5at_1cd5zia1f}dEIB`||_j+i~oFPFwK zLyR%J2aX!;4ki2bWFK<;o4kRd8wi| zDbBryDmZZ#p>H)_e1`?M8|&W%dnd}njv9;02aJ6_ML#5Ak4T4wV-gq8c|$<4Eo+nU zA5J@^7Tymxj^?R^l z(-3FdDEHxzNWw?1>Rm8KHF#UBnozIQw?!-7t*+&bz+ny)Sd8YyB&`0}t^a^B9Ex*4 zmge`K<-9vasoAFli#)OOQ#4EooNa-NGxZTMnR-zSN@{NnT#~5=Vqyu$Ded(*&f`|R zyUO=tBgH3pg(kKu-h=Z8D)pa7YtB7t)tT5pjH=()s_KPptPe$3o$vX)2J!d6^Wnxb za202RSS7)1Q`M)#$7Nv3)fkI}mB+hOQrXxpFPECI9mTmG4x$A#?>SX}-{+e6xzG8z z&-;1#;Er^fw|PN7U(F|#mt*P}o&04>yt+BU0~q0#aeiaKAc5~=pMDG@bHck~ zBCdwYT}u50r&9mb*vwF1kPGqwj#l+=)V(c`Y5vbt{kYG0HgGu{`WRJ@9;ybj$3WxR zL!n+Y=m0gCJ_Z_3ABrI*8^VFYrd1)K>V2cUP6-W=O|iG;V3cZvleU+8Bp zwf?MAtv@z)N~q`()N2D9dxQ-Rni!LbNFZkZBX+$%=Ymq5E0 zS_AzFBKc@O<9zy}7-5~;+-g-l?nqTeM5>~N#$KX@Cb#>%Z&h5cI1j4a9>>dzb-o~(f+|U}PI=}Ei097xE?P{Q*agRKV@#17t zm+=Dr14DusK$b!c=#HlX=T>8#3HoAfB_+JY zg)Zk_rT#1!pW#}U<~{0q_;w_PqmGEgRQi6Ls_d(J-*>I}BMeA2SF}6QU=i(f3w%05 zSn^BwhAdePte_R{qnO4AY%L`0$aVTXrU!qlnRQ5^6rOzo5xtpBb)HwE?v62jPxE(v zpm|#`^@j}Pjg`PGu_JE$We=#!8d(lfR2~g4W0HiAHN81-6I&Jb-q<BTX|AXWns`MehbKSDTTlOjQ|+|F#Ef@S9>Q8tcS z@mTnOuZwXgr$53lr#LTY-p^eR=R@N@Zv#$(sIG}ksqZqoFz=!{Y? zJhjuI%GtY7KdDNrOImu-!yP3k!Ah9sZfO|Qfb%_?hcu%1l*0WiNZB6kK^tJdTy%F| zOhQy2oQYO+JNNMTX#ms7M3*IXl|L z#^rB;CMHo2I($yVGa2YY4|d}qi83uN{G+0BtzfZdlyK7{JeV|Vdcc$Fsy>8c zXG(Aid|G7XJ^_B1tM_byGt#4-a6SC>MGQ+9zy*Ax25(Xlur7a&>+-Ie)u1p^Kq!jA zSV!&A0E+i3RyW>NT}$IgILQNuIItwnhbf_Z5c}YSd40=Roast-VbTC@JioQq@YU4K z&r;Dto$mxNdF?w)(fjtnR?{$8qt*wZ&sl^%2-t>UtkL?Qk+osiHr4td^npSUH=y4m z^yw>uv-+dGwNPK|rrd`h?VF107!IKOE0XyDFdxtdnjVIF<@7?_g?{K-aR*$GrW=z&gXcpZPE9|eBy2@L6qc)b4OewN zw4H$yUa(cH*mI_ksMPGl!UXU>{d97jmT+!Tu;`}NNeM+a!NSf%)+n5jRP;0mm=^1* zxrptB9T6xSiziM4(|UA@x+%e&n~X`p#W%HT-j5W0Zmg^NCFosEIEO=g=El#0NW6&% z?G{%u<|StkKJQlceuZF$IV4rIGh@{D?U;1I1zvD1|A=byQz=yqifxKX z*OdEm3IdbjOcH~E)(DHxJTb27PuU)babdh_)%MQ)pR@Voy2+uyIEOD-(&|en zF*uWgD%t-dou4b}s@3^5tMk`u=dybhl?j*N3~MlV#VZyUnJ%-7ej2o9OxvLN;{$&*^SQ zVR8%m{f1qi?shwZw7?J4;Nlc$U~Hl4|37?+ezM2)*lJy`Q;iKD0C|4V0D| zoDu_#VtBm^Jp}{I9nj|vG)2UkZr%<6)`V1*$HFg>6j^ByY~X$u+(F$T2sI4aUWax3 znFZ3mEbG`P=&+6{;w54otpJUy8f0qYC=GEAiZTqmiN*_$g!zi{!W!QqG6?&ih2CLV zXg+wE0SYa2&bcl!jId;Q1I#HIOqnMOiYUWxw#wATJ(S^-Z-L6knKE1`54%FXDAqbsNmd7n}$?!IxJfl>~%@=Q--JOJLVvJUrldc=cB?98jg8us>`^ ze}Usv^g@h)Tyw!Rj~gTCLVu@$`a%lUEY4ufB8)W)EedNEbsPqL`Yv@Z29tdp2DkZ~ ztz31vAyL(DK+v-ohN>@ipf6y(0;?`XiSW2Z4y?K?PQ=24lrYRA$X7G1{n$<)U>FdX*5Iz_+%2mC2~`Pg^` zDMvY!2}hG8M^x z&@#Dhiu5hB;ag_Iw|ooV@<}H}wjr<(D{QcrD9zbSl_8kZ^j!Fs*`{x4wtP!N@+r%= zEQD`q$LM$*qhroRQlaP2%|2509*0Y(LhF6b9f8ZI1P8c%`T)n2V742DGIj}&K?Zy&Nwu6Hs6{>ho3+ECU9N}CNE$3p!a>BWwwZ@JxeanchzD0#^*#d|A zj_Uo&RdcwrLWO(5(G@K?2R`OYELN$wu{e*;=Bhr(ts}g`Jk6+0P!P3Y*Xb`H(rd%5 zslkU@u`8tprx;qok%)f#DEe(M)Z1XFt6&pSzfdm1TPoj+FK=di-!M+_0JMC{Y zA1yQymf8kOHOBfv`J+U;OzPCMr!7%EnNx5H3(ltb{rVoeR}XT2wf2E?eL2jH~7Wzg|CVsRM^YQgBynjX!A zHKS1z4b0&>kR1G`La&^3Ay8`;J{u?9sjPVFQVY7-qoF#V_tQX@FX4+0pMgN-hw5p> zXYZ)>7chxBf2%Lyq>7_HoLgW@Etcx4UWdcsysiOW{j2iN7*c@!hnn63-kg6A}_^E%$+c{5%?Mnbg}S^ zSoDHd677q|4c{uP$2L~166r8j{6xH%V@15kZyf(SohBlQztVd$-Z(E_5URqF1#S^u z*C-UO^&@@?^oVfm9}2vvi19w^X8h6OQ)2PSFiRU{BnT9~H=%-X1g!5=1mEzqH@pGw zvlUCrjSgcZQn&okNJUa%uHgxy8mL`3n{S*qoP8bso^37Hzy&J~HENg63>}4Wcn21qn zWcVuUn?Y-s?r#i)R{b|wnE=keM0q^|>a9P=xVXnyh|xQ;zWl)7to0Mb$``ok zl+SgLZ#5lZrhc-c!%Rg&_7?QF$}opp^g#?2z+mATQxbFiG(qx{>jU_@8Y`#XlQ^ft zaRfU$SV`A{H>(NQR(ueM>9I<}yJ8=z{w(&QwzwX?774YW4<396CP|zMsopx*!y)=N zy@6)Kz4k|1IG%Vf%*Q6?Srw}|-lI$6H1BEGiY7WY{RG&m-nU(=>e=T)^U+nY?BLza z^t-s~gY*Lx@7Jys>ySW6_*(V;)3pi*K+rJT)PxVQZE`EzrE^=F*w9h-wsHG}D-w0{ zC!!8Yy@7AGwgR6S95I|*3i^mKn86O`;4mg;a!?!2fOZT6+Tjdn#~@zWf&pho`EK+d z=@>()VoNNJBlo0WdLmqW`l!}P!R*8~Y_^PQql&`4I3w?nX&_|4h zOi7e!G-XPnOrt4N5@i|!39*hk>#|FmV)( zvTw-{(J1tMAfa8v))3ofpKDbkuxbc4A=>5*vu*Zk-hFJF_gpL1i?(@B^&WDq!l@&) z>sBpc50-UqRjZD)YR;|6JJr2yW~6{HD|sJRcP45$*T^(j^4xoFaxj|hgFy!Elg;+Q zF4yE>4%;V(?UT>;$+z0)4eCPfYpG&8YnN-qipLdy(C8)c6{8)?JQM%tLRFiu5u zBNul6>~ihwRxY*?Lnov8 zuqSD>_;+~>Ky#kZyjbKA`vVCdChwEmyrD^mvCbgI3S+q}aD@wokDWqpIPY!265M(u|jgCeGhz0 z#gD`uFr;!KyikJ!Zc+6Cqt&2x3;Yhk0CaPT&aU2ak=f@}=eLUfAiRfx^#=3HZx~*U z*v-}(5Z#Lbg_V1Xt%b^+l07#x4V62K${j=Hv@2cg$7fKvH##a8)||VR;tIR-H>_N=j305x7jZZ$hkaVCuR5E^b3kIIJ^-sx zS|axgc4*AAT!QxWN zaZpdV?!jlH{rh^1LL?(;A|_wkjjJ|TEW_lXSwzHM+l>paSj0C;yop3>OY&%hR!iCs zMB#VLL^c+_OO!AQrPd?I0M7~6&-dZ;)Gfvfk!)%Ovb_)Wd`D)4Gbs}~$^0sk367~0 z>)r^D6c+u2MN~-4j1c2Ue1Jqt=3x=qw}qhjMd4j!#%Hr#Wj_6~rH04Kku6?i>!Rl@ z$oyL{?a*_SB~}*2dL_bR8H@e|Y8MrnLE_^PVm^u6Nwj3XKSEPTdjYhxU>BKj*t@ID z6C&A;+=pxdWTR14HN^0@8p8iXbJ^{6n&_RbXY0d*tByqJJMKadu@pVm+7Gtq%Z~8h zF|Q8~kVzuvsjU5n>DV~tbO~n$_pWgxMEF*0Hgn2~H9QQR!5GDrv3t`ce8&b_JP7e5 z82kPLC0Dg!QE&xmc}0ltuz1rV>Tz7fX?O)&WX9nRnBVpoZve&|o=NM(E7k;#8?TDj zM!r5TGN7w6Ru83Al;KBee0r--KZBn>nXcl%J^8Bma7gg;LQ&9CY*kyIzz;;w3MldJ z$PZjt8UB-#To1wAn4XLgGo)d91W)(xPk>ECzBJ4u#>ua=ehzP;5#)i3Bhc04h~A*5hzJ@;7LaD?}KzurPzqdbtJUR&=c2AnNuiHUk(bIe5j z#m)Q(YJ$FYGe3HopjU6^7f$2x+0^Lq`ry~OdW}kG-geij61Y#UjA2C{a&mRb%|#~G zW3eaNvBYC(oW-!ZuGjna()+GZ+UwLjq)@!u7r)Pq-LIhq9+eA==W*i*o-}~>_+;xF zd+>&pqfYIKW4KF8Xp1Sucx<|rwioI*v=nNMtz{pn1_n6S>)Wg9K8hR@jQm!SB9;uk z0o%|9PmBPn#WQu~0R?RmXQE>9G@hnts0IN|Z{>#-%)-M_nEQ)Et88r6;*~$s4(qV? zwcQxWcfE>s%0nc~V*DC7eL{2ub^>NUu;>+l#R0)BO7L%@=hTynNye%`jFClzS;X7_ zBZ?4~YEN?m(%@4rJL*X!wUs1T^|qHL`q4CCf|v4p7FJeCSR<8#p+6Lj)1g4+p(Rj1 zZqv&T(7_(X@*&kT)dGu)KlBpAu8fC-G( z(_e`(_JW+&zAdeNd+^e9Y`gre-!+KNwS59t8;kQ(d8-Hb4RgIW|w&2X7 znTSg?h-i-+Z7=YrSXyy>N#gJwT#gSfx{n* zRUj;8!N8~$-2pY4n?BvD|iM^fJ(72E1u-ud;JT|=| zWk{q96_g<~u0e|U7m{C=afj#kED#r`uV7+0PIG=o%JXwcH(E7*2~_ZuNT7Z|Rkc{aAEAc#N9+Fc74^8YRAS3H`ziuIQ@Lf#By+1ixVK00_jg&N!aQ z-@X$HO*oStbc_!TK%YNbkpXSc+|Y#4w<2DsxC}3gsB~s)xsC_3LIV~>v-@8* za_?A_0dD+?y>9;Xmbd=JfL_)6_)~|n#P+(C11@dHqT;|Ky4&IHRsL?bmCJz|=nm82 zE>d3=;~C}M7VUJjZ>6WLxr}xXlM~fIdxt?bPC~opZ`IVXrlB`T!H-j+-3+30~WxB)x7vgAv|3*2PrN_Okv)q52?<#IsGTip;VoX!b&Ml z=U|bl+OwJ|y<5tYBWuM&6nq0tbomg4pTIIf6@K-CnJceD_d%{F0JPj{%VKe|GQea7 z=g?TLZ{d3@YQW4$!30+%A|ot}G%42UT|@&{)8HjRDu5Z6BB8M!iecQqYWck1vcW_; z{UG)-VYmu@se=i5u}>Z(cN|T>3Vneel7P<+^2wosk(mGk646EUgJ4;?gZYSOIkn8s z?gQLPyyMust9lX(0)hqc+01985d3o3T2B*D#_2!ELI|i;4WbY*sKiEzOktH^bzl%J zzOL#fwky^=mO)hJX3pc`mj!mcXB~KQcLvi8@e51Bfv-S;mMJic)!*UB{dYn}%XD7< zFACPl8ohs%9b^$V^`U)H@dVR7${-m#2g2jlB5C-wb4X_&i*`|?D4>-!|GNo1M_?Vh zvsLsfLMIsejtMNi|4i`n1+RnmQiCPf7BM}S&XkttviutPl!2*|@S?Nhu|oG`YLvl` zOt0iM;k3ZwH%X$-6j&Rh1FVeEO52D+m!R9nO4<9U9_!8&#!Uo}Tx1gqX@57##2B%H zEcHVhuO%xh1WRTu>>(Jd6A8g8O+Lb!VwiMU14mK&=_a$$Mbi#^2ByH@N4HF&i~{U| zd4)fQA^Ffc;lF_#F>qKctnzcCuaI$#?7YyxYDxjuA*4a?5Hg4Q!a=PP4lO*Ib}5Rc z$*}#x%PbUBA-W*;$?{=KA{scyrUPLm8KWVZDA+a#X4FGwaxzwmBrwH2;>GOG=*N?k zkf}YG$`+9vAuWCtf;^V9@5muhcpkVi3U6ehMn-|f`v$NSKeXSSt26#bs9BcL^0MU( z!Y8dhg|dX>LWO>92{}}}NFi9E9W3-D3vFG;s+zil4oL~$YU8yK2yP-yq)?YwQ6CDS z6Cq(33D4S^QH)tT(Ej1UVmndBoOLYn1gh9_xZ6SYU8shG>~GtU@sIf9s=-%=4sK8% z7r~5z%{1c~A?{jf?D&rEG>**R#QKMkQ`j@X2Il}mV{hO`1u$Ut0UpV~f(jfl8tfLf zfinjXDiP^4gFQH2{mSpD3@oe zN(f@F+^^^-jXuyQC%U_zZf}1L%5-evgs%FqNaD)CaguGu<-)$qIN(#XZ$|Ye2<1X4 zzq%V*ATMJGH7MSC3qvCAE?ORbIMC{>)=$Ecb}MO|Z_G#eo$D9buY3R^;rkD3J#pG} ztcqhH1s~hJ#kDJZfPyA>RX+!LeL;NFBR-wmu6i3?%hv&iWBsc0BiCPxvH$hK{M$7B z9O|hie83$5{K{rXNPwdQ*izIF`Jzg23PA~;paA2qb1;Eq@)}3 zK+~|LhO&7EfuC9=yrbglA|uJH9Xn-{`5ioNTU+Uj>5LlF&BRX z857hqIOyQ>p4WmN558#lh{sCs-5|ap9H4C93cG-Td}RCxZGx4&7HnG_uKWtefA9?e zgoX{d{#prt_dDvB=Y@`898(i;h#s2e~FQ_=sn{FZHKtq*tIKacEZzW`?Zp2zxr z>bEA$uXwN;bA6ZnZFYw^=u8G0pK@^~-?p>e@q6Q4zR$u+E+$ymrjk`iK`EFoq}?43 zN-@J#;@PoY-8p^lNcy{Edhz{}`Tc_UdLb1vm$pDM#l>Q316NkDGj^Wm3+F)XIOBrA z=nJkUq}#to==3eZ@4*}0G+xXaJHA;^g8X83>QiRt_&Mxob>!F3tG>YY0Xi*D@v;lR zcJTot&Q+wks^6sB3{J!uJ$#~NMH=|X@{sUTqU**RXIUtO8pThrc&Z~+3ogXicMh9! z8cqZ7U_Li@|A%HMJX>h+TX<(zfW}p*cef9NKWRW@?vEb`HZ!q)db&dqVmXD z`L3)v8B3)@-`1+p06Q8eReICFd*Kx*Z&mQk>cEX(a6R6`q!>^M)YIzPlt zabQp+XMQB-H#koX9~^HSfhaAT6*D%8H}(P!PZY-$Fl?gkZVs_;pmQlfIJsHk1@%*c z0aPRzt!NB;5=4e1Ct=bp8>!5=uy#QrBP>>0EQWMu(TW0%mM2UWFM~z6TsSUr;l`(s z%@Sn1#pOLDByUrY=gH*{7MCJ&xdB|HEV<-@&l2&495<0Sg1~DCt)HJ4pQ7b^_p6v> zxh9^~oM$!f+4Ac<(rMnd1(>o!ANa0^vk4q=g~7E~4#C1{%Lr1`e+ZU>X-66yt~$?7 zVf+>XiPdkk4x5>-7^Z>^C@45El$$JZe8(o3=p^v4Y@7fqgi6MX4uWsQ(6r8I$jxN`RjOcQdLNetz0Qn%~T-X;* zIu6FpSdPc-s?HPUoI^QtDCgn8Krt(Ys5uaI=Wi$~-ssa<0CKZ4LsF4|43*#*ae|jj zyypYyN&>nlJfjlIxCna1M+3$95z*F7=xy5N$;VjF)FePQi;20|G z661l#S-Q~5aGa^fO!-dqyYaE%kG=(g#tkhd(vqX#vy~t$`3chjq2laRg($?xCmULF zf*>MQxp<+#sPI^F8D*3pm7HxE$;gFX4c8z^PH4gSk;Uax7^Ns{vsujfc+Xk}J*G4m}zKCXpk!aft$h+!Gy@vXYW_)0N;nC={a? z{-q8qg0Aip<=BXI1V`i9(th)&(}Vs!ri^mD>c9i>R^yqHy2dkQO`yEgR!YhrTJgXF zV&mC|LQBtFXvg!hGCnVS_B#QekL^7#8!W%PV_)N!IUhHEaL*27DtrsdkQu(dATuB% zGc-K&VE^h*o959QHbkq-zFi7|%4@}sU^kwN?L7=~HMT7<9zi2Dp6g%Jgz}ANpoxs& zunhf+wBYU0L>~lN*C(S$Y0KdnhwXe=Hu=(L+z*Z`!g?;1-uYcxhVI z7g1?dZ+@HB-jJamt%P2VTBHSnACu8b3Dd)w3MkN-%2H4d33g!&5DlyYs!#E35NK9K?Gl=jI8cIU}Br%xX2UwJN2uCUZswVk!a(F z`ih~GQR~>6GE}hf+&w$fs@lJGJ#v8+ui751=5230e@9*8`TjLoWM9=5>ss|tf9Ubt z<@IS*pSP-c?eVz;9?Dja&v|y02k`|<=#D5V49kG&zC74i9ri{uLmSSs z;LOmf^R?ia8Cr9mqT#I8S=?vP^nEYz`xI|mc}q)Qd$qYnShE@m!Xv96iqdOhuNIFc z3@W6J6^|?pKLhr6no5-Gku6x8_2r$%uP(&6R^^}zsUh*(oj9^JL~lBM5GU2^jb1ox zpV1V`gm*mST6Hs0W#IQ7>e3VTrBxlhDDCv24CetJ$X~(pPiUrra}hLW1glqYqbMV| zb_KV8(yE#tN~`+#p|s$t6=HMe$U_tG4MElC4>h*rRKF$qR7=jP^P=&C8w6r;>#*Gh6 z+zne(;!Z&0*T#Jt)i^z88}2<&NA&?3HeOa8sO1aPEGqzx_0;tmsl;bcGf}vfhlml< zUq9$YQ~I<)rd=RSwGg%O$e<0P9*t)Pnfd^`-Hl%t@1&4Tjp5>b6nPJ!y;>elB}34> z9a`RT8n_TQZ(!VR*yOmlkJxIf4zShSA9rmCMQ9AsAc;~V4JP^1r?L0CPzI9{p`)UvNvZNbE~$+djMx7gGU z%AOq*J)7QkJiKikf@0V5FW{zpdDzdxLd4(M@m2oQ86rJGfra$xaAJS(IUJKhBOZqf zKEQG^L)D^+lksGWOb)FThB`U4O7xA%NXPEDIz71gb$U3AJP)M@H@r?Sm{#@KLz9Cq z3YGix8_wW9hu>9wU3$h(A8L4JUXEGY`Um<)pPd*QJB;~>mp)^1P4%UEelM zAAx)9SurG>DX-RoqC#fvwVpB->{I_= zcW(n9RdMwJ-z9e;K*(-1*jPaq4H^W}4G9ngF$-BpG+;DXiKZ-JvytHD!|aBF4TvUC z)?^`1pU~PGY|zl!K4{YxTG|Gi$Zq%$Au4SVQ6r)xf*KVqAdvTe=3{rW*#%Kw-{0@O zzhuwcbI+VPGjrz5nU6ack10kf#qc$)KnqCT5XyVOlQ?4)_ z-ceU=@wI{faCwj_Q{C4ytp7y|U~Q`xih72%FAq%2e_>)q#+@f+MX!NkK%m8IqyVzO zn+mx6o5>|oM7tMlinRceiV5nSA{#g|!6o*PcuXo1kFyI-HCv`y#%iGzz5qI>|AF6( z56r-Y;~H8%d5-nNIb+Y?;f{SCTK6HzTH|~?5;~Mo)517sxTfu<1Cgd4D1YxnB=u|q z;Spn;f{C(>D(0D?cmF>5Q#Gsg(C8ZG6Q;X1Q-5b_!LSI*fR~WY$2@1m;|!O(5x_k| z3qJvYP_EdP$I+}utP{-j$BkRv&HX~t{ zhcd#ih}oD;_bXy-b@bEheQCh~kLlLDy|{Dh@d)!R7300<^~_K7zH{#z-cMp*^PY<_ zci?ArGnh~x`>qCeo3n9u!VKaals)o3ng937y&odHv4e1DVqf;Y6Z;;hcM!Gb-HNz> z!94va?sMX>f%pMdbjTFp@p{kc=iokOwUHhoXB~hhHY%Cnj-s7IsF!Wef_sL0L-Tpi zz&W0=j&|uB-A+%dc96?_9+ivnKZYr*aK|R;xZ!%Z)ZIa`9xh$o0|-M<2Lc#^G6Jq0 zYXR_)!F1P|N8h=P1_qH;-I}UBt-WnkXEeI%UD)u58ZEBFRx(JvO;6csOLBaIR-uHo@Rp9^vT?&)wETIhvEZqS^a$?uKl-Ij67a@U#t^54HJ;zU4huW-sTi zuA^t?V=C&r-uWMZXh$<4yt$i=yf(q%{QM(GezOq|Jl64Xn<+0O!iEkaefSZUInTSJ z?_ky1>K&iE5gF_C{wufJ`=i{=WfZS%^!$UAaMOFjao@n$?Obv7hZ?WTaX!*}#_?vP z$23&`A5Ysubnnm~@>tJY)vdYRRr_8u7(&YDC`F7Ju0DjCJ#$q%o^5@PTyqb6vX?a8 zO^!Drz3(`_jP(A_aT?sL`jZ~hSp8?7O+V2$yvIfg=VIn`c#!OI4QP%;dS7>(i==dr zqx{EkMB9 zv-)JsIje^0g0%Vce&H@d*3{>yUF&Mco}=98Sz}MHVOF5VO=CSV-l|zm!#pwSVy&O@ zZUipfZI8rweGQ&+Q-X1wR1%uLXK7Vyjj#rwtktqd=k@j4X4nE? z8ni~{P3oJpWv~RmY}NLRpg_#(9t`^c-8B3I+>mW&aHF2X&1D>JGZI9TZzec#!SK;$ z7!K3an`fyo9}9MS+V08g#`6swoc_5wKr`CE5vc{Y|JBB`us$eW=4|^+fF9Tr8KD14 zCV;4eO#tx&-))S)NS8Hs48WoJZjMK$iH^8Jh-jX*pLbBk$#xLed*(k!8E19_io|G6+b&vk$Sx%K-X8`%->FQv`kR)LJh2QVb3iPsTu zxIur48;Q1jG7VtX@;O3;p&6yYe;im6YqrO>k?<*Ljq#RM`P=k=m?F^K{K zVFF-h+gOD|!a3GsXyCqyo0<)z6;w>s?=8k--%L(KC%Pmk4NZ)}=CEn98a9Cja}9Y^JNBoxwG2UV#H|5ptC8t1$PwSa*I49(^_YV zJ@rcz^Go znYtHJvAGIwXHNT1=Flk_uIZV(sN+`fU>P=1^CuauGkEE3Kt|Q^h|Hmx*wZ+~bX z481Fky~_StXQsy^~s;+h$ZmN!+!y%s5S1}Xi{p904-ZM*E zalip)tW?j=GgoCA6b3RdLd9JfyK~X2kqj}IKTY6IBl(??-|6{XD!+^4cWLa-#JQRT z*KGc58K<1j?p&*68kq|@VyzFcm?P>W!oy5WKP|ydu$Rpf36oXIhtYJ zGY4-tPkp_75@z4B#$xkt?9cGuffG>YxYU?AuJjlz1gFQ~;D+>=xXfMYF$P4&Y;9Wt zosN14XBkvofOGvQcxTlN|M^0L(z`$1wMY0~ zYJN?xCzv8|kIB+Kvvq$N5i$Gm5m3q~muc0PYDHYe;Mm<6gXiszq#r%~05xy70hRoQ z2@ZcR9d2+ab8zfS=~MTYuicqU)zv);0uo#ZSQlC!x6i1V_6yU{DX!@zzdF*n9PEMX zOk1~FbzEnHE;3ad@!D}yfF=r}SGu8wpiy0ORjA%|j7TMQMHBOL(pDY64pgzO+llHL zMind7-WJ=OxsBh@GdL04%o0P>t63GAAbnv?{&Bw%S+H&Xz`#`@pd~75oRTpm0r{{nmr$X z;r+z%hU#tm7#+;=m5Z}IrYRq{(lZQSKgP&)`O1t0k7>-uufpnTR4qn3b@8vLD_gj2I!*=M7}VM zejBOXvOuQ{7$reE!_!;wEO1N)QY1kJ!&5_Nfi4+HC1F_+B8B;}^l*Lw{XVf(kUXIh z57Y9MX)HtY<0zalk5MkBa9xw6+)e>to;W2jks|KGNJc+`o-Urx!gdNbKPxF)MBMGC zB_>M9KHos}WqB-C8U1eGB57Jh;MsE$qZhH#^hB8*Prn(d^t&*Ne$y5*ypw*{l4+zs zV}l5+Y8HVyj|i+iBxz1c8bu^g7b^m56GWhK8UmS*wx$8h!G~ra8;x!d77*&sZ7Hly ze_L7yTWWSi>CLVzy-Gik^b=*;m9E!Q8^QrNvBH+N!j`W53GC?H`+as4CUVt^AUk@U zFr#W)_ceiLR2$NaUO6~+?cjN9Rs2WdKMMc)J{I%|)8N<|vY_jj1>Ms<7Gfb^-Ae$a zArRZPXg}Jsi?JW=n&t2++!GrR#(p&0e1Mu;+wa4ZO?Z+EE3yhD3ZwA~cF*h9J&my0 z)y78crnc_uP-+poO|P6WeEPQ>9E=r*o-d_>&Zyt(LVOjGxitFX^clW<4IINne{~q&GACr%qw@Ic}$v{|^_D ze+lGo05e(%@1YGsT><-}tb4L*eoN`5BP(;PZ_J4}ik{AXL+`A|;}g=?#v>~%qjMrY zpdN7SEaWBKpP7HscA*~1481Ga6*e;?`e(*_1~&EVBLnb1Loe$i?0RtaYDL)|iCkjZ zbL5)I3=Urhh&w$*_+n0nJy1J3;rzhR%oa;OrwCdpO+OLGWA-PI=g-M;vx) z^mCC2VeMD}ptWNfw=*)m_SeLZraV~m(hZD_C%4C&K7W@tEBAo+$i2rrrfGSv(?rxp zN(@s;_n+|Q z4I}cNj#=GG%xhTTU?sH!ciTH*5^FY43Ai>K0s@h%wN%!iuhw?pT|i)JwW~O$R@=%k z_1ZFy!DI->G-wxcOoO(OW16(t9Mhz&116)YE4!kRgcIyn6L(KrIjxGH#k?e?ewJ3n z&z#n+YlA|Ys@ftmt*>~`VO5-FHNXvTpums$`<1Bep7u6Syn|)&HY|&u({G^Xd|4dS z_3jSji3Q3q=0u>{t$$x&dgP-z`N)Vzd{ujQc^lqjc4v}{?LB=j7G7T|pFPLACv)s8 zx4T;y4=iuHi7A%1c`9dP)JLI6>Kgn+{gkiP;>AY2_td?N2U2&Fbfj!lVFc?Qy?>pn$G5=!h0fgL z{vj5)aoo#qK$Y%lP2MxM9iyoK9x_F=VoRrP%@fR0=!e%QqkwB#*0ELW<58f{AUg_wcsrz-0VT??sYGT0zWXm134Puf-8-I}QtsPmejnp=Us=HR7 z#tr6;JE1gawp{?8QFpyjn?%{6_fS2X?s}s>iPpZ^!U~pf_R^9LCemqLr|rzj*XZX! zh7~Vz_hR9+jh6K0SYN}zn*dlI>@~n(wlE#_E-&F3_l#Oz(!kXgp(Zy|V1!y&&ODp& zbR)wd)byMTiMS?{y$cSp{ghW@|I+)q{j|rVZ*O1gRoV}%^~}6A?iG#4l$CcF)1(c< zs6>%!lT3aKD|bqMD^H%1;>lA|EO~TYBtf-Lf)*|TA_)oFuD1Wn>$1P(&9(3LK%5Tm z;`Z0qdQ8UlqmV$%(QX;WG*hMoDOz~ql%y|lG%sqrl%6l|Ej(pP&zCaSBv0F5`vLDe z_M_hAb}V#FvA^yuYd?mR6WUK8<&m_~P#Lpz7-<<*TvHOM7q3E6N^kCkvtG>`UaG-e zgl*xUEYT-dP#%@VlNU?#JzIu-ZOJW+y#xNEh}#qS+8NtaGVi3L5z0SQTg-Q@u_H*6-R5G zHc<8MCP}M#MIpqSAFDD^AXDsz>9&^D8*yNGu9^=)JB;ne*LvCv?JqL9|ARL4DvA6$ z+X;XGRz=RNJdDM3k{o83H=)zCwC;I#WLzgLA2khQh7GB(lFhW@Y^$>dCOB6T2-@xS zWeei8aETGB=aqqwkSy1}3#1J`G*^6Yw|zI1>zr5LzGtm>d;6}no_8n4?WWXghcOdV z)n>p^Zo0=_{}M){h8q&zIo5DPQo{{N4KpOkd)(Qu{;!f5eT+tpw4TXL4cwzN*zvx` zDAS#?Cd0LjEK-KMo*D2A*9HYWa+d2YVVp9?_Mnd&eqAECxm%dA$E+ixao4gw0V^KN zrhx-B1L8ytF~eQGnyf*cS=ee?PseT~W`JoY(ySWZy3<|i<7WD=sb+?pb}Eta+ykQ2Em!x<)sC= zsYuW@p<^BsMfqDzmRhdP6Z#6E(_JsJ)%h1;)Q9zsLl-3pcOfmB9C8&FDcMuRpDW4+X=f9*E+oV0_N-uVhU?rXOlNmfp zuo8>=qtHs3-GbJ0GmbzDiw{P^TtcEV-Xi-;WVDZg-840LD{2yLuF z`t79Ag43CjElh>8tp6s-VQoJoHyAsgkb2&lRLbhxBm(ajk#apt8QW>Nph@;msc+c7 zmV{!gAQW#5-bd|cz4^soQkrLk3?Y(6ny`Us?H#bL6XOn3Q>bROLcPmsJya9^C118* zhIw^zc%;ggPn=_I#Rn7aa7V4drV$pa#`!2M+t=_Ca!q@Tj&hm0v-~&~?a^ib@n*cL z^(sQcc2TI++jZ}&nN{cEx11aTGyZPTs2BB_?g}_(%SRnHnA~>`_Pm=v>O~{Dq(*X! zu(Xs7h}E4QbmUkEhF=R84UMW5K(sx)RU{e{Un-JruHUhBlh=_x+Uq6~!g0~VYbrhjeznPe0Jfp83O^upDwWGcD4cbF>m4Wp^)6Rv4A888ZM z8O=RK{Wj((k6SSEzA-dQGK2OQz&p-n{u4F`@FmuJ>VVux^Av!a>o}6F0z(D(#)Hb3 z&CP@dG!ooP+YF{*n?WVtsX*Hd(!A%e)I!1^y7!p(uzru{-G@dU)Ki@vfsxN2bn*-u|6FSA-gookTApg%bLpoY|#Ihk( z9hM%;u|1(u&*}zCkLB2&fa2jviR}roxoj%~M%X)yl(~Jm8>mRYgLWuHVTZ!?%i~%$ zQ#s?AnN(mKrQ`Lm9SYcy;K8~i*9p$y9Cj$2)3aS70Xq~bF_b|~cKZtg(X(-w~%3P`wVvlvdXZ4*+v%vsz zEPzv_(=i+QU_H>Lx##J7vDX~5Y}oFScCA4U*c-IMt~JR4dy^cnZ;=D`Epou#A_wd( ze89d81OIGFdl?4)+^&fM`^GvU5^={F{aTOd`kNQ1q{U@;;u!@yHNFFuj_g#!_#V6t z$86X5I$_zij5o5OV5Pogd>L6b#Hb#xC(DLV?f7gsJl=<)>?j;>!vc)vV>WhBn&+^A ziNoiZ4A`P%r29&Yqd9c<@aJwL4(|j0xokjB+szBG?vRyt3LLiSfx#E1rh7lozv0Dh zg)+n-({&VPEO*ogFk^R(dX>zWyBX6$1nU5)%Wfq@wmKVkCl>BEe ztquc1?a83AVj(od)Fb(HSJ!A^)o4RSr`S+2n>JJkeK27c6s%eY6^`Gkv1;*IHI@*p z8bMgKY^p?9HL5(aYT3L%SOGD^2IOWV5urXA5w~bNnu*YZ%~<@|jxq+hQ7gkUZq$$p znbr+Pq9&sT1U6XQ?k3e>My;9R38U77Fj<>gUeEL9Kc^>7Wadc0=muhJAokvZ`Oi`J zO?ZN0Lk0FyL}C5HaRKJ$VeF%Tajf6Llm}ZaRPO-&>tOI4NfW%GLKr-@&5F$42Oioy zp)$L^gNz>z;-ftlD($g=O}(D)u|PWa(T0jed_zSu;~cd0ii^Fv4$zEq_G{UO3N!~M zKeR=U2f{#k&tXFa=_RtnH&pnhC2Ka2R$yPWzIyW(Joe2~$_*6?N)HAuk^wfEj13hu z4;`?fVo&!kPeKYT^*;7F6{db-iGdu)?ngf*jB3lXxU%! zg2{AqO`(d(%TI3 z_nK>J*|?C#hkQ@=JK}AXt!$nT<2ktTIH*+nZ?xgrZuM#!CE|WqJMMAXYxOCT<uvN__ zSk-F8D?_@+=A)a(3w^qEZ)<4+a;}Y!<~asTek% z#`2E!)k0lttABk)H)ezfW#Lf<--^3Jxu$MKP?Fm9bnhd7%$DU$>SQU2i)c)T_&QDJ>-Kv1tjEOfDr{9h=4~ zhXP3tQ{vUJtEN*Sfo|1oCJr#!5a-bs=>bkL0!2d+vps=U$Q7zrNUfB8!>&qE#2i)S% zGJ5ww{NXD`l0^Dp1k+tj@g2|}9Hg^lKUJ8n%YB)CJoNJ>{T!s959#Mvp>F<# zLbGoEZu&V)KPPAE#|~+ zXSqkj^DI%pWZ`%*DvCJ~hvr0d&@si8Cn5QB7?!RmWq)1@k*^? zy5-}Ff$6%0lPC0PtIp4Lo-(cKo$LGvT)RJ=t<$G`K)>-Pz&!VT1m~xmq~P11nsvDy z6#5*Y`G@J}6yZJxP0ZF4lyk87H{CUyq&vLaoP~0z!K){iYS<*RXmiXxiP{iM}CW%kB;ax9;v_uw{1-Aatf_FA*THWiL@6uyrqyAkeZ`&>X?dzwo~w z|M=b)7R;vZD}M$zhw;A~|F0wcLf!D4sQyC;;xH>mYK9eWmvfEDD0-RXg=F#cncRpr z)WiJhPD+@Cj#3EYB*ZH}h8Y3pK++*IY>kIHF?slbDG&Q3kDb_rJ(4*nHf)XDj((4f zX!qL)z`AsA|B`ovVt)ah7xTmIFeOUH-|?f6Q;Wl=QRQ0A)rwN6D``z`MeD(9%!OIH zlD#;*{WFv9{a3(qk4cx%TByt0TWG==>0LMHLe`luVkgnEF6=Fwsrz2~Nt`|J9gz9x z`Bh+BP6k$cB2H1*`^jBr?q^4N$5h8Fu#8xLVDEn6eh7zY2Hm#hNVZ=O%Yvyr%2RJl>B}C!YKT z8hL!3`+nr_<5mziB8V1`!kr_YmqUtJN8Oie#8CnXxx8BU3fJKMxpw83eBqF7_8@|Y(;7WTL3YG+(6H< z?#JjpL&uV`1rHFYPkEM6R;M(J7~28qvqJbdlJ2p4VVLl(Qr)^qs6JE8BWl%oJw&LF zUyB0L)mCDz<1^jO$8`yNT_1d=Td)t?&TuBCb+4{{56+>fW`(Td`%TbyZ>7F_YhCrV z=*Vlg@;*FoEA`!58wJt;BS)0fcW-SLNZyC%ZA~L&t3akv7f{uXNzPBopTRDlsolEA zXr~rU?9)dR_hwX`!Isf~;8hVR;(jLL`{(F`WY#ZWQ|p-wkTuI)TTgOjxzp?QFx?}b zpr2pjC+b(3RXf$Jsc)5cAtgr=cpU(nSF+sIEH?Z*789oj)$3jApIqu+p~f+>&GZEX zh<1{7e+M8K-e6DeZTkotY)lcS_*YY2xqy!?Siiu!?NU36wKkrW4KJ_H;6KDtHNtXH zk;m?174xEzsxQh|e=V|=yVq46)rf0aGuO|1RB~ zewzlf!SEc8>ejpnXAIN9nMJ!B!G%PK>TzC+7Ybw3DE)&CNR!kzJqp#1WJx|GLd9lc zts0wD@f{l7Iyx_!Y$-L4O!p5l7{K{5&~qF*wjHWsMoSrn)eIdJ-}K)%%n?U2tuN8Y z1BbXZQ;|%$oJo6I6Y9rLOj`kn=P2Q?)ATJ3g3(j8V6xQb}VvoaGQr zN^g&fHMPJwq4ouTq#hYZz23IAWJe>kJ23`#M&6n}-fj9tf;{PB? z1^6NUk5P|`|C2M|yFA?lg_wnQ`t(*FXn5Mg16!Uxh(IPrVEE9H?y+*1h^Y;%UUZLr zz#3pHJ97>>$Qav!&RtWXrM{yWdpp$1&rPm_x4XCa%9)qJT(=z+;VTGqwCA85Xs49l z47YtX%k&%yP|i21l+TSS&lPe*5^p4lsj<$PdZheye80$H;T%~bf`>!OOPD5E_ruWM z@hQ;=3CyA zPL^raIp$=+)nZN-)KQ;Og>>`f9#*YNrAQ|(&r|goRyb%dJkwR+ryrx?`UO zgX+gf9tu240aj-1ujU-YxHm>f~0Oy-dAH)vr*T-H*Tx zbG_I-08fGJE@smZ=_oSM?Q;JM<_=Sd3B#YK>TsQdzg0TyX9$~hIWPKMutRv}h0}Ei z|D7;shfTWqfcL+G@FC%ly$F9ym-ntNcVE|-JFx=}{@51yU@G}!FG)x2AF$rqa`0yx z_qrC&Lw=d(rNAP0ZSvi!xMe%`h<*m>yC(Xc(LsgWG1JjGIWj-AzjfcO2BmB2T$G)A zVk^Sf-@1A{K+Uc1{{R5H2fKIRDT{XxPSeT3p>a4FNt;SU_>u|Ens<*JiyKTcWK^Aq z?Cr>?!AmHodn-Pn$oKErSyx9jhTCvD(Gu)oPG~ZHM{(xmsd1-z@ zQHjG@k1ak1t0xtUp|57=!EM>a4SbLLo>o2k4!e>o@m)^3pomLmhMrKLp$-#qss>;7yzzDg!})&sKH55a54 zN6}Au1>Y<9HsqgeFI|*x&nzi&hLhW}#A?rXICyo4puZ%0ZsSGTbK75jdR~uxsNx=j z8k@7Cyu`9J-(F#JkfJ0ympMUPPFi9sdBC~YFwHR0Xr%aro00FZISfS(!`yqavJCl7 zXhnf-nc)@#o|i7QmFL^-vu9?9CZPCtRpi^l#a{|VD=oj}7Rww{5r!}`)@<2U?A6` zi*ofBmA{U->;d=RN(uq`z!L~WdytnFoqju+f2p=!+A}-E%{-D6NsNw-jK}B&H^TyY zuUJORals{J6=o?eEwD&0D8b<@xaF2b`Hmv1rKk|St}VZKnssr0`8Wf6LP3kOJio~4 zVCd=74L6Q6$k^m#E-EdxkMT2?j}l~n`JtJ2T4rTtnGH7w_32zP{_8A#&{pC|bU?8a zSOb_~7;8wmIl)&h7m4fV!_Gv&Z8+%ne|enX`^Yqo4+RY-MY-5N`O+8rPXq6Z{qr@8 z0T6)Z0Urta$1Pjakk4tPjoDNM^UVsT#Y5673gyr5;|G@urax$)%+l+hH#|^eIRusy zn@C9rH^Z+&k9--%ervN(uqfj87fboDGIKX5c z3a1m$4%h=&O@&q5+uH!x0k{t^tpxOVB`Okd7=?031Iz+! zt?BJeK{<2+<^d|Tkb~~mAs?8%=mD$-Y_10#MsfNLNFQ+FuK{C`4&WBRv__-@d^X@X zocyXh4Ssl!i}ngT0S$n)fJVS3z%syNfU5w{0X71T!@F_Kfa!o8fKI^dXOKU@I_%MN zA|3j!4$YnGUqF2VwgReG0pALKfSqmN2iW87?Ty7NREBoQ4cG~o2iUv|<%8#CZzEs0 zU-drtVb#fa9Qg%QFntw;MZ6Bcd4MB;qD%8|_T7qRnSS7t*WYYVu8A5HNgqJz^E`_Y z40=*S1d;+GE12q0DrffgqIi5)147gA?<73*Nqq)F(cF;|t*;%x199#M9x$v^c_3lE zmfoFLqK#Dpc_4MoAeGcCx^sp~I}V~#+Xcl=4igRt9{UB;mpRn$<1mtwbMYY~T0t{sSs>hcIMPiDum}P@z64-V}i7#a_&)q6UTdhV`T+1^BNN@tYA=9sc<1r2edr;FW8rQaMMXT1gxYLO0?BXT8u3k%F;G zT{mDo^$c9jV@M|pa(3Wey@AqUI;dYWKqy3nx(<(`^+NSz)kLfrpfLRht0RaXsQ-k| z1O8wLyc76+A@H@pdqUuwfNu%H(?J&vz;6Y<2T)y)6h--7Zu?@Dk*%gu9=~Su$_{K@ z>fvvV)6Y>g--=9BA19Gfr>%cIQNL&pfgb^UK@ff%_{5U^0G{c~STuH@n$%Oqed>W5 zWgh<1L4OYPWeBU+5j`t$BeN6h2C`NU!$(E|icDZl1Zx^Q8DnlJ4v>`iwjn)^j%0e* z08A|UchWocHFPq#D4z#`j}mx>#1HAqN#IrB>!2^+P*<|R;ajN}bu9$@b~sghTb!uO zs2oS2J1Btv6Wp#-fX@rUQ$3FZejf0R2&;F(Ns#q?3lO!uX5Cs=r>ONCf!xO`^<_r4 zpWGYL9;)YPBaa(Nax{T&Lr{8TAIa~k2fh_y^#r7p>7@g4F}>o(U1}Gl*G@i0zr=x} z<8VmlP9mK&^mXcbN{1};<@e)E>sM*iFQI>5x5`&(`dFG~669;ztRPmikjrm}6=Y)i zlZP(#)Y88C6OU(3;7~@x4erLobx*gevK; ztr@7^1t~-inIJk~!^NF4W0iKzK<<-Ke(jJywo=%$6XdLOd7FTb0^Yzd{D;c11$gy` zz>_R~S!pR+I)BVJJqEgy*h0UGvLopF`w(g&^)>xm2A&+VvRHtp0&-3W47Wd_xeP%3~k!)hk3f3B9Tsz&un@ACU^todR8D ze>&f07m`nVR;kar*1G#R+_vzMq0=ZwRVqvNusM zK=mQ;@xa#x;i(^*27EQ}X$XhugQ&^(>w}1#(yJ8l0esguglL|n5_}B^tKTD~V``>% z4^SzZ7~oTHCi(}^+l74UXXwOm5uX;|3nZW8z+Z$<0q9SHejeyoA*}8sdS=Fq%y9Yr zw`=iOI&NrW{k$gLs*JTNd_59I(9%817l?Uq4N2ZK%&V!TedOZpX`P%GSugEMU_Z-* zf*VEeOZ)Ap>~V!XjT8NkUX~V@3O*0IT=#%=5n$bp)M}&E1d@@}KYD;z%zvR6eAfts z;(_0X_dtz=6*fg3jW9e4r+9t@&ztG-Sbu(eePiG_oi#L{oorTllM8DnWk=4FZDw|| z7IxBuv>G6n6Z3Xu5MDh1eUP%z_$DfX>>l-L`hf$MD(dwVidL7d>Rh4MsY};(u28>E zs3>)5&)X~X!_=jx-(I0!jnUaYW-VW4zx&zm0j3D;>=!d*yr@|MjCWriL5aj85zGLY z2`EgHa5tOX;fJ+@Y9u=1SzdmGql@aJ0r;)JQ%@o<%6C2RwRqpRmFBIE!X_bhgYAKw0v$*q|0 zObf^#<+~m~M{p2^-0M&Dq=z&Qd+_-nJ-i7Ipodh=Co$-s&nC!s^53$)m^6LnkVQWK2I<4*ll12#_&EO)q(8!r;jz;D zq)9V1Z8fZiOV#ckq&%T&j}B5ctJ=+x%HP%HYa^BITEZOEYCj*Oyu-pDX^HaZ1NR9X z?tQXZqxqetK{N5w!O9n!XEe2gmF5UDZay3g^Z)4(t!K2dVW{?l(aMgY+BXxGT|@t* z!Od@mZPz?|lk#OWJ!#f$*F2t}td60ZzsGFXoJ>&GUr9HfE4OR*k5PU(oNitm{u@p6 z&C2TUT^NYF-x;)z->B>`Xn%jB@|r=r_eSMS7Cz4K|2wLelKC*6(mxxog+M^P-RPPD?~bocP$9VYFjl*+U*S*Wj#gtN1u53hN8H7pZqG}r!<=X)qE0} zd|gZ{A-=|&$@{J=!U$b zQ$A+l&H5i;W9#>|Z+t(Z_xnR!*F^mOnwws^MtSQR2-tPa7zF=!1U=k^-w_e#M-a(R zM^gA7Benk?8F6+bVXCjC@c&UJkqw@0lto}ZbQWe4Q`$vOp#wEE%cS|RT}+p+~1_lP*MT!3w-t~jb^>(I$+jnw110L>IT#f z_;85wzN-D_70P*4`>!jM$F&sxz4nymO`Y=gAg%j~h~GwP+hQVW2WxwD5w8x`0*^ZR z%@xY#A#n)5J46c>zZ(jse;#@}g1?CxcRE@*95n&q#%S%)Xyql+!;ShOztu;)qR*|0 zQ9g>H@VP6tX+RUM%mT|dFdVIk9IIWRJRF#>pK3HKGJ*KLM!Qdq_}KvM-&Ezm0PVlD z5x^!A-qZSG;Qehu_qPY#&kMR=7<6A6biX3#z9#7Y$)Nkq;+~vuy2`LJPZx&2 z>`MKNL)ad6v7>_!LIJZY1FerPbja)~69J4g*!890kC86BX!VXRax3XdxPg}!^+a?v z3PQLQ>>_u8F2P3WNaptvpTHbx>r1zzYIKr*Tf#3YaM1bOGlI zxIn-X0apsRPQa%Gd|tp81l%v+TLOM0-~|DrO$xiN6);i2=>pCbaDjj&0r1zzYIK&k*Sgm?+?M0p|+1K)@0KR|>dJz^4R!UceUw+%Mo;0)8am z1p%YUQ^$3!fQbT57jUkC3j{0?aHW9j1bj-s=LLL0!2JTgCE!N_UJx)kU8FByqJYx{ zoGai00ZRm2Dd0K*pAztS0bdYszkqKE_>q7Y1dKL|^aV^5aJqnV1zaFtiGV8wTqodD z0zNO`3j*#J@GSv967Yh6(X&MQ0wxMLUBI~lE)cLpz?A~76YwbkpBL~20rv~|mVh4# zctODE*&=-b69t?u;9LQTY5*r?zo|EkVsL6j$%7@OKQ5X2jo<9)|8C3F8DJbIsyM7(oY@pUF3gK^3)0V50U=% z?L33h(%=5wq>uKO z8nXSRj{8pXkM@@uvi)uUZqlEG^e2TX|J3o{LH?5`Cr_pSQ1$=+ar&u=-+lh67u2w> zP@~MAIrA1n!tA;Cj4`AnrX)@_OfV)*FeWD@8xrouOA;A)qmkjqr;Jg?7uX8(E9_2t z(V|=MzpP|&sl#aq#4anb5N=XRK_B#G5j&O^+bsn}jsxalT`*l31|31hF`DmMe?~Z#_BiW@Mim z3!atR?D-Tygv#trC6QipR1%#wyo;Jxh#NdBEx;?AN}_GCrLa7|*k)N=fVlDwOs!Un zZJE_p=CnXayG==?mq{HEfrBMhkP{0^58Rk+`Nc(6L@h-s_(^0*^JEs`ozO&UX)(QG z!@a0)_InMY55m9n#rTe8?4{<4$>#b4Q&0nM5T;?X7XLE7oG0)>5g|gvljC*CCk1#K z`pfuo9znuGIv$beiAKhkX-Hp%|Y? zci`4eD+A~b zmwL4yfnXuyN%(P4e1jNA8*UQmhiQL;o$@&X#7idAD*T}4L>|A314*tJd?6k<3lK!Y z9?19;f*uClDzuI+)8-uc@6jJ{%7~x{LANaJpeDu`EKcQ=8Pvhy8j1f({L6Bac8%tX zL#5xG!{cji0ztqPoPNNge!&PxC;QRBhY_$gvVZK0k5FX4*B7rUvOnvK*DA7~>WjxB z%MSuEJRE_ZUiK@2Pa{w&vcKqyAMEQ7`r?Nu(!Te_4^^by?u(C7q&@D7AErn<+ZPX` zEA42&g9t^ZNV^z-M*Zm(Y47^tV-#u6`r@xpq@C)EzfzI*r!PKMk#?moez+p-MPK|? zX#b-EQ7jyR)ePCb10P0UwL-SrzWA#Z*&h4iY1d8Pi}Z2;JgMOFr}m{PpY-;n=6gXj zXnn!uLu+FirGNRPGJOB?p=nP_ca`Y3WjgmWdcXX#-H^YcQLa{Y3p*p*<1)cV$&eg4 z3@3jz;}fG~2d!WH55wyer|1VIw_h`SfBDJ1Ao&&1?@RhN1VWYDdB(@Dx2Hq|vQrw3 zUk}p-ektLR-^D)|#W1oTImGbbBN+Ti(7z@4vBF7xR;n{&UG+;3a(wjHw3t+!#ckEbwxkQ_|ll z@CGseC;2}l@N!;K(z_-9Ao@Q@{sJ%QUy}UA{F>zdq2w>{l727>mdPJPKUUzk2hqf7>KZyR0JQANP z@UcPojRJ27!gmY&IDvm!$g>y*kob25(eD!YBLYvGFzKSbbwuA2M1L#rWXI$_1_KvY znN9St8+KuT1X{U=;fE;(asY9y0-S^P(rAeu6H^r%~X4#L-Ft@rRt}gq%eJ z|7VtNobs%wcPYSWxhtCYauzi1Rvp!D${@uRnGSl zJ$7NN!Y^G0_KR2k1s~PG@nTv;`HjHG2H~Zgavw{uoQ*+p9u6bV`$C>Z4Nq5&dnA9k zKPH%eQxN}ij1T$+QSTyKN?a7>_mkdWzbKL6`}@%u z;5UcCw=#VH@m+fu`pz);FT>ysC^VMepnADS;9F?|6IUzyRg@3ur7UJXNk5_l@Ny49Rc z=J&=}j&CjE_zn(~TLj(^RL*wbLzT}T1pO*OZxDPAgrR>U4F2;l`0vB<#(9=Y|HVM2EB+o(ML&-A^ z{YfbN%rN+S7#{M7bfr9-fG0V#MgPU#&x1^#2>MPp7tkQ&91I6LRJtR9zh2M;f+>uC zD9$Y6Sefq6!tnW{px6I`)6@Hmbe$3S#uxd08h00zM3_!A?S7XDc*<8tQ28%s^l{1- z;U}mfrc2NpMfs-*{NI2NmEV^cpR1L%LH$UV;A0T|ph57_VoD_xpV49PX5d5RD-U?4 zw}QVc&-IKC^tqKM*eufZ2>ND`FN46p$>_yR0Js_sWGFe4!{F^<@NK}8oQl)jF;%L3Vdr&J-UFW{-|yfr<3`8O3>?_9N)-+a)i-~n*cBZ6Su^t zwUr|~gQjQBGIp^;B<7J16{uZK2*JU zfY-ynZ4UB#I|QFPk*?H}t}uL}t_#ioCg4e)x}fsU3q!w6(6fa*$lbxDSClZg%Qz)F7Y4z@0^cn3T-NW6Fv3LNTFvPtel_r+hnzwE%egRou8j{Z&$KZ3M}enwJ4$)F zqWF}Z0?%HG#Um-t1xeq;=~r=}Ts=B;`!WC@syuH2p3+r5^8i9$1PBmf>z< zlEU}w;5gIb(5){AaO!s%A|>w2`nR4i*+&f zuj7jo)(0(#lZ~m0?=iAzwb)D9S?(29CnHImXiV}`P)TqSijHSS|1;ksY%uf1o}3gu z_T;2X#hz%K;^*Hl?Mq2M`67}}zNF+6lO~5xJ86>tS;%}(oG{T(5iI$H6#ui3u_sLq zpZ285mr8rmr0}ISY0{-iZBj~j$tR^;Qt}Dm>wD6KOV;<4@cEuNqvY79_BWIE&`;MGsi?m;t5t+qf9DI?d_`dd#xx}2~nCE9ZH^(`N zA77oB<4mE0$1}5&k^;UjlbuiU<)m25=1Ju?Iwsm&jJr^*3&OLuT$($jG6|_nwAyX? z<>sZn6aqv{mB(8Xu@S`SNuddIE+fHjgNMIpCi#mdK=8|vCn=@x_gBnwr&ui5<7=@l zTb4B0Vkx2X$?rDrFm$sq;

wBpMTwCQOD1 zQ>>*WOXrnlmN;`9zO1FP6V=g9zG{M?^EE*SDYnCG_HpBt_(S$#9t)(mL1tzy(kJxQ zd{lJ^DnFLKJ`h-v_XTLDWErk8NvXoc&yfU|uey#Lm^-Gq{t`?w`p@16YsVs45&27( zSqdvktn_^!`Z$p@zXadnVc#X9)BF>&EE9_C#zK2(zLQ)Vn=>&vbt39%!US@S>2qC$ z6>z@G%1aknz^fp$z&y|5ukUP_6koxIiQ~vD3>SxL$6i!o3nL0Tko0@8_q{L?t$Eop zOBr_KmzLm5LeAxurSKnTXWco&lx4Yd)~p<)FwZn2%S>Z{0!OK3aehewHq?s&fpE^# zbLX1oWX?qP`s;|VLP$%(7eJ`Ygywgt2A(tpHVfaVu*@s7pq~OPNP~sqIPSi~*Iu)&*-MIwSem7Fhq)k#0q>LPMUQ-^b+M%c?>1OcD$>$^ zXudlq^Uk?nGvmp~c!5pi*=Ej0-&oAE4Wkz#7jttQGtth1WV>%}(v+;y{DOH!#kNe} z7pBUo!J*nwu=tKfkt{sR1o#=G*K?UEpgQi)E@>ug6TaRx6Q7bunn;FR{HLoSJ@h$@=lHWZaQK7(aYx1Zo5WDZf-!PCDe9aiB5(?Mp3p+a4%#z=vR;U&FU< zkc~TX9242VvA;r<SVJFd&{x6mie!T8mNp(|RvxfqQEi6>3&S2ZEbGZ7}U zu3dV90p>f^B0hKms=%%pw*~g>{%s`30WSexxLXRBHmJ}o_#_<~kEJlb$SxkqR>$6k zvcMV3x8U1-WVq7OV9!i5GA&7o$w-TeF@-tGA(Hf0bqeWfNK??SzJ2EWVrKSh)rDV? zCox6tmvleny{HI&cZ0bq{UWpO2+k`yh@eu%XL4+I6gM2LVq3Ap=2W1+fhcPk&0Ddz z>dH!gT!`_M|AsmeZdc&}JA$i{^Kqa;lTl-3#Gf(ymYhTgLb?DOv#iJ&l&er(^QCNy z=(76_*;pRQqYtzMe$D9D4>_nC%5mBq7~ z@i+5+Iw9Rf=3Dq`VFDA7eAAhsI-~$2$26b1wxF5{QCcFYq~P3Hzh34-_Q(Co)ZdaW zR#y|@w~|Gt!BIc?fGz&!y_x^3qg1w&#b;qBOvI<=(7hGIyf5V+ft^{7Fa{{*?8%lT z?2~rci&^bo{)7Xo#o!{5)ezRp%W;-FLXD>QSS3K)(B&{~V^bYw6w}?9_khm?&7+Br z{u<%vuPBA(He26@_Kl}>{eA<_zw`uT#xHkBZk59t*0rVLvtcklDHAM?in6lOa!k>a zTPF>Z4R~ic`a)q!(8I6~jQ+ot$7~FM@)s3h=uyNw{+y!8@G)udV$Qb84$o%+zLgOz z%5_3=1@pxMTzD%Sb&I3u?kRKN)x)q^X2Anuz92dCOB8$!t*E4LIRv(ZDR<-RdKh+H zvNbF%Qs$WEV`XKo*^*_RJ9}P+PtPwtL4vV3&3hzU6o+}U>OarIr1Xn(8BRSPsj?ni zOtM|H`Z38{>ag{%uMp$Jf`Ab|A14Y0Op+S$)oQkXKLSUv7~h%f+oZ+%8s;l37y*bc zJ!2J(KCEqnY4pz)gqa}-GB~cU;sF32?{xG6!!eU&+>`&?iI5$Y08`1kF&OItNL~RwIEraWhiV5K5|QQZkQs+Xvwe9Vm>LPXHIh!cNYa4uTZ1*{u89E ztOBP2gV{jEzfws~7T;g(uPxvp%|VzBLaZwxr3uWs!@}d+y|VR7TY}p`2%>iM<@Oy%0+(L3Lg7i(;`MOHe09&-!#?{|}kU BX;J_H diff --git a/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc b/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc index 432a528bb..a2dbff785 100644 --- a/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc +++ b/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc @@ -5,20 +5,13 @@ #define ENABLE_ASSERTIONS 1 #define DO_PROFILING 1 -#define DEBUG 1 +//#define DEBUG 1 //#define DEBUG0_1 1 //#define DEBUG3 1 #include "template.h" #include "utils.h" - -#include "define-float.h" -#include "avx_function_prototypes.h" - -#include "define-double.h" -#include "avx_function_prototypes.h" - using namespace std; class LoadTimeInitializer @@ -33,32 +26,18 @@ class LoadTimeInitializer m_sumSquareNumHaplotypes = 0; m_sumNumTestcases = 0; m_sumSquareNumTestcases = 0; + m_maxNumTestcases = 0; m_num_invocations = 0; + m_filename_to_fptr.clear(); - if(is_avx_supported()) - { - cout << "Using AVX accelerated implementation of PairHMM\n"; - g_compute_full_prob_float = compute_full_prob_avxs; - g_compute_full_prob_double = compute_full_prob_avxd; - } - else - { - cout << "Using un-vectorized C++ implementation of PairHMM\n"; - g_compute_full_prob_float = compute_full_prob; - g_compute_full_prob_double = compute_full_prob; - } + initialize_function_pointers(); cout.flush(); - //if(is_sse42_supported()) - //{ - //g_compute_full_prob_float = compute_full_prob_avxs; - //g_compute_full_prob_double = compute_full_prob_avxd; - //} } void print_profiling() { double mean_val; cout <<"Invocations : "<::iterator mI = m_filename_to_fptr.find(filename); + ofstream* fptr = 0; + if(mI == m_filename_to_fptr.end()) + { + m_filename_to_fptr[filename] = new ofstream(); + fptr = m_filename_to_fptr[filename]; + fptr->open(filename.c_str(), to_append ? ios::app : ios::out); + assert(fptr->is_open()); + } + else + fptr = (*mI).second; + //ofstream fptr; + //fptr.open(filename.c_str(), to_append ? ofstream::app : ofstream::out); + (*fptr) << s; + if(add_newline) + (*fptr) << "\n"; + //fptr.close(); + } + void debug_close() + { + for(map::iterator mB = m_filename_to_fptr.begin(), mE = m_filename_to_fptr.end(); + mB != mE;mB++) + { + (*mB).second->close(); + delete (*mB).second; + } + m_filename_to_fptr.clear(); } jfieldID m_readBasesFID; jfieldID m_readQualsFID; @@ -89,8 +93,10 @@ class LoadTimeInitializer double m_sumSquareNumHaplotypes; double m_sumNumTestcases; double m_sumSquareNumTestcases; + unsigned m_maxNumTestcases; unsigned m_num_invocations; - + private: + map m_filename_to_fptr; }; LoadTimeInitializer g_load_time_initializer; @@ -174,10 +180,10 @@ Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniSubComputeReadL tc.c[i] = (int)overallGCPArray[i]; } - double result_avxd = GEN_INTRINSIC(compute_full_prob_avx, d)(&tc); + double result_avxd = g_compute_full_prob_double(&tc, 0); double result = log10(result_avxd) - log10(ldexp(1.0, 1020)); #ifdef DEBUG - debug_dump("return_values_jni.txt",to_string(result),true); + g_load_time_initializer.debug_dump("return_values_jni.txt",to_string(result),true); #endif @@ -255,7 +261,7 @@ JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPai haplotypeBasesArrayVector[j] = make_pair(haplotypeBases, haplotypeBasesArray); #ifdef DEBUG3 for(unsigned k=0;kGetArrayLength(haplotypeBases);++k) - debug_dump("haplotype_bases_jni.txt",to_string((int)haplotypeBasesArray[k]),true); + g_load_time_initializer.debug_dump("haplotype_bases_jni.txt",to_string((int)haplotypeBasesArray[k]),true); #endif } @@ -310,11 +316,11 @@ JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPai #ifdef DEBUG3 for(unsigned j=0;j g_load_time_initializer.m_maxNumTestcases ? numTestCases + : g_load_time_initializer.m_maxNumTestcases; ++(g_load_time_initializer.m_num_invocations); #endif +#ifdef DEBUG + g_load_time_initializer.debug_close(); +#endif +} + +JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniClose + (JNIEnv* env, jobject thisObject) +{ +#ifdef DO_PROFILING + g_load_time_initializer.print_profiling(); +#endif +#ifdef DEBUG + g_load_time_initializer.debug_close(); +#endif } diff --git a/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.h b/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.h index 1fff9fa5a..b50f6a5a9 100644 --- a/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.h +++ b/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.h @@ -70,6 +70,14 @@ JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPai JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniComputeLikelihoods (JNIEnv *, jobject, jint, jint, jobjectArray, jobjectArray, jdoubleArray, jint); +/* + * Class: org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM + * Method: jniClose + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniClose + (JNIEnv *, jobject); + #ifdef __cplusplus } #endif diff --git a/PairHMM_JNI/pairhmm-1-base.cc b/PairHMM_JNI/pairhmm-1-base.cc index a3b5e0ad5..3030b640e 100644 --- a/PairHMM_JNI/pairhmm-1-base.cc +++ b/PairHMM_JNI/pairhmm-1-base.cc @@ -5,12 +5,6 @@ #include "template.h" #include "utils.h" -#include "define-float.h" -#include "avx_function_prototypes.h" - -#include "define-double.h" -#include "avx_function_prototypes.h" - using namespace std; class LoadTimeInitializer { @@ -36,16 +30,7 @@ int main(int argc, char** argv) if(argc >= 3 && string(argv[2]) == "1") use_old_read_testcase = true; - if(true) - { - g_compute_full_prob_double = GEN_INTRINSIC(compute_full_prob_avx, d); - g_compute_full_prob_float = GEN_INTRINSIC(compute_full_prob_avx, s); - } - else - { - g_compute_full_prob_double = compute_full_prob; - g_compute_full_prob_float = compute_full_prob; - } + initialize_function_pointers(); std::ifstream ifptr; FILE* fptr = 0; @@ -86,70 +71,5 @@ int main(int argc, char** argv) else ifptr.close(); return 0; - -#if 0 - float result[BATCH_SIZE], result_avxf; - double result_avxd; - struct timeval start, end; - long long aggregateTimeRead = 0L; - long long aggregateTimeCompute = 0L; - long long aggregateTimeWrite = 0L; - - bool noMoreData = false; - int count =0; - while (!noMoreData) - { - int read_count = BATCH_SIZE; - gettimeofday(&start, NULL); - for (int b=0;b(&tc[b]); - -#ifdef RUN_HYBRID -#define MIN_ACCEPTED 1e-28f - if (result_avxf < MIN_ACCEPTED) { - count++; - result_avxd = compute_full_prob(&tc[b]); - result[b] = log10(result_avxd) - log10(ldexp(1.0, 1020.f)); - } - else - result[b] = log10f(result_avxf) - log10f(ldexpf(1.f, 120.f)); -#endif - -#ifndef RUN_HYBRID - result[b] = log10f(result_avxf) - log10f(ldexpf(1.f, 120.f)); -#endif - - } - gettimeofday(&end, NULL); - aggregateTimeCompute += ((end.tv_sec * 1000000 + end.tv_usec) - (start.tv_sec * 1000000 + start.tv_usec)); - - gettimeofday(&start, NULL); - for (int b=0;b #include +//#define DEBUG +#define MUSTAFA +#define KARTHIK + /* template string getBinaryStr (T val, int numBitsToWrite) { @@ -17,8 +21,9 @@ string getBinaryStr (T val, int numBitsToWrite) { return oss.str() ; } */ +#ifdef MUSTAFA -void GEN_INTRINSIC(precompute_masks_, PRECISION)(const testcase& tc, int COLS, int numMaskVecs, MASK_TYPE (*maskArr)[NUM_DISTINCT_CHARS]) { +void GEN_INTRINSIC(GEN_INTRINSIC(precompute_masks_,SIMD_TYPE), PRECISION)(const testcase& tc, int COLS, int numMaskVecs, MASK_TYPE (*maskArr)[NUM_DISTINCT_CHARS]) { const int maskBitCnt = MAIN_TYPE_SIZE ; @@ -52,7 +57,7 @@ void GEN_INTRINSIC(precompute_masks_, PRECISION)(const testcase& tc, int COLS, i } -void GEN_INTRINSIC(init_masks_for_row_, PRECISION)(const testcase& tc, char* rsArr, MASK_TYPE* lastMaskShiftOut, int beginRowIndex, int numRowsToProcess) { +void GEN_INTRINSIC(GEN_INTRINSIC(init_masks_for_row_,SIMD_TYPE), PRECISION)(const testcase& tc, char* rsArr, MASK_TYPE* lastMaskShiftOut, int beginRowIndex, int numRowsToProcess) { for (int ri=0; ri < numRowsToProcess; ++ri) { rsArr[ri] = ConvertChar::get(tc.rs[ri+beginRowIndex-1]) ; @@ -63,6 +68,7 @@ void GEN_INTRINSIC(init_masks_for_row_, PRECISION)(const testcase& tc, char* rsA } } + #define SET_MASK_WORD(__dstMask, __srcMask, __lastShiftOut, __shiftBy, __maskBitCnt){ \ MASK_TYPE __bitMask = (((MASK_TYPE)0x1) << __shiftBy) - 1 ; \ MASK_TYPE __nextShiftOut = (__srcMask & __bitMask) << (__maskBitCnt - __shiftBy) ; \ @@ -71,33 +77,43 @@ void GEN_INTRINSIC(init_masks_for_row_, PRECISION)(const testcase& tc, char* rsA } -void GEN_INTRINSIC(update_masks_for_cols_, PRECISION)(int maskIndex, MASK_VEC& currMaskVecLow, MASK_VEC& currMaskVecHigh, MASK_TYPE (*maskArr) [NUM_DISTINCT_CHARS], char* rsArr, MASK_TYPE* lastMaskShiftOut, MASK_TYPE maskBitCnt) { +void GEN_INTRINSIC(GEN_INTRINSIC(update_masks_for_cols_, SIMD_TYPE), PRECISION)(int maskIndex, MASK_VEC& currMaskVecLow, MASK_VEC& currMaskVecHigh, MASK_TYPE (*maskArr) [NUM_DISTINCT_CHARS], char* rsArr, MASK_TYPE* lastMaskShiftOut, MASK_TYPE maskBitCnt) { for (int ei=0; ei < AVX_LENGTH/2; ++ei) { SET_MASK_WORD(currMaskVecLow.masks[ei], maskArr[maskIndex][rsArr[ei]], - lastMaskShiftOut[ei], ei, maskBitCnt) ; - + lastMaskShiftOut[ei], ei, maskBitCnt) ; + int ei2 = ei + AVX_LENGTH/2 ; // the second entry index SET_MASK_WORD(currMaskVecHigh.masks[ei], maskArr[maskIndex][rsArr[ei2]], - lastMaskShiftOut[ei2], ei2, maskBitCnt) ; + lastMaskShiftOut[ei2], ei2, maskBitCnt) ; } } + //void GEN_INTRINSIC(computeDistVec, PRECISION) (MASK_VEC& currMaskVecLow, MASK_VEC& currMaskVecHigh, _256_TYPE& distm, _256_TYPE& _1_distm, _256_TYPE& distmChosen, const _256_TYPE& distmSel, int firstRowIndex, int lastRowIndex) { -inline void GEN_INTRINSIC(computeDistVec, PRECISION) (MASK_VEC& currMaskVecLow, MASK_VEC& currMaskVecHigh, _256_TYPE& distm, _256_TYPE& _1_distm, _256_TYPE& distmChosen) { +inline void GEN_INTRINSIC(GEN_INTRINSIC(computeDistVec, SIMD_TYPE), PRECISION) (MASK_VEC& currMaskVecLow, MASK_VEC& currMaskVecHigh, _256_TYPE& distm, _256_TYPE& _1_distm, _256_TYPE& distmChosen) { //#define computeDistVec() { _256_TYPE maskV ; VEC_SSE_TO_AVX(currMaskVecLow.vecf, currMaskVecHigh.vecf, maskV) ; +#ifdef DEBUGG + long long *temp1 = (long long *)(&maskV); + double *temp2 = (double *)(&distm); + double *temp3 = (double *)(&_1_distm); + printf("***\n%lx\n%lx\n%f\n%f\n%f\n%f\n***\n", temp1[0], temp1[1], temp2[0], temp2[1], temp3[0], temp3[1]); +#endif + distmChosen = VEC_BLENDV(distm, _1_distm, maskV) ; /*COMPARE_VECS(distmChosen, distmSel, firstRowIndex, lastRowIndex) ;*/ VEC_SHIFT_LEFT_1BIT(currMaskVecLow.vec) ; VEC_SHIFT_LEFT_1BIT(currMaskVecHigh.vec) ; + + _mm_empty(); } @@ -120,8 +136,9 @@ struct HmmData { } ; */ +#endif // MUSTAFA -template void GEN_INTRINSIC(initializeVectors, PRECISION)(int ROWS, int COLS, NUMBER* shiftOutM, NUMBER *shiftOutX, NUMBER *shiftOutY, Context ctx, testcase *tc, _256_TYPE *p_MM, _256_TYPE *p_GAPM, _256_TYPE *p_MX, _256_TYPE *p_XX, _256_TYPE *p_MY, _256_TYPE *p_YY, _256_TYPE *distm1D) +template void GEN_INTRINSIC(GEN_INTRINSIC(initializeVectors, SIMD_TYPE), PRECISION)(int ROWS, int COLS, NUMBER* shiftOutM, NUMBER *shiftOutX, NUMBER *shiftOutY, Context ctx, testcase *tc, _256_TYPE *p_MM, _256_TYPE *p_GAPM, _256_TYPE *p_MX, _256_TYPE *p_XX, _256_TYPE *p_MY, _256_TYPE *p_YY, _256_TYPE *distm1D) { NUMBER zero = ctx._(0.0); NUMBER init_Y = ctx.INITIAL_CONSTANT / (tc->haplen); @@ -157,18 +174,14 @@ template void GEN_INTRINSIC(initializeVectors, PRECISION)(int ROWS *(ptr_p_GAPM+r-1) = ctx._(1.0) - ctx.ph2pr[_c]; *(ptr_p_MX+r-1) = ctx.ph2pr[_i]; *(ptr_p_XX+r-1) = ctx.ph2pr[_c]; - *(ptr_p_MY+r-1) = ctx.ph2pr[_d]; - *(ptr_p_YY+r-1) = ctx.ph2pr[_c]; -#ifdef DEBUG3 - debug_dump("transitions_jni.txt",to_string(*(ptr_p_MM+r-1) ),true); - debug_dump("transitions_jni.txt",to_string(*(ptr_p_GAPM+r-1)),true); - debug_dump("transitions_jni.txt",to_string(*(ptr_p_MX+r-1) ),true); - debug_dump("transitions_jni.txt",to_string(*(ptr_p_XX+r-1) ),true); - debug_dump("transitions_jni.txt",to_string(*(ptr_p_MY+r-1) ),true); - debug_dump("transitions_jni.txt",to_string(*(ptr_p_YY+r-1) ),true); -#endif - //*(ptr_p_MY+r-1) = (r == ROWS - 1) ? ctx._(1.0) : ctx.ph2pr[_d]; - //*(ptr_p_YY+r-1) = (r == ROWS - 1) ? ctx._(1.0) : ctx.ph2pr[_c]; + #ifdef KARTHIK + *(ptr_p_MY+r-1) = ctx.ph2pr[_d]; + *(ptr_p_YY+r-1) = ctx.ph2pr[_c]; + #else + *(ptr_p_MY+r-1) = (r == ROWS - 1) ? ctx._(1.0) : ctx.ph2pr[_d]; + *(ptr_p_YY+r-1) = (r == ROWS - 1) ? ctx._(1.0) : ctx.ph2pr[_c]; + #endif + } NUMBER *ptr_distm1D = (NUMBER *)distm1D; @@ -176,14 +189,11 @@ template void GEN_INTRINSIC(initializeVectors, PRECISION)(int ROWS { int _q = tc->q[r-1] & 127; ptr_distm1D[r-1] = ctx.ph2pr[_q]; -#ifdef DEBUG3 - debug_dump("priors_jni.txt",to_string(ptr_distm1D[r-1]),true); -#endif } } -template inline void GEN_INTRINSIC(stripINITIALIZATION, PRECISION)( +template inline void GEN_INTRINSIC(GEN_INTRINSIC(stripINITIALIZATION, SIMD_TYPE), PRECISION)( int stripIdx, Context ctx, testcase *tc, _256_TYPE &pGAPM, _256_TYPE &pMM, _256_TYPE &pMX, _256_TYPE &pXX, _256_TYPE &pMY, _256_TYPE &pYY, _256_TYPE &rs, UNION_TYPE &rsN, _256_TYPE &distm, _256_TYPE &_1_distm, _256_TYPE *distm1D, _256_TYPE N_packed256, _256_TYPE *p_MM , _256_TYPE *p_GAPM , _256_TYPE *p_MX, _256_TYPE *p_XX , _256_TYPE *p_MY, _256_TYPE *p_YY, UNION_TYPE &M_t_2, UNION_TYPE &X_t_2, UNION_TYPE &M_t_1, UNION_TYPE &X_t_1, @@ -200,19 +210,18 @@ template inline void GEN_INTRINSIC(stripINITIALIZATION, PRECISION) NUMBER zero = ctx._(0.0); NUMBER init_Y = ctx.INITIAL_CONSTANT / (tc->haplen); UNION_TYPE packed1; packed1.d = VEC_SET1_VAL(1.0); -#define TRISTATE_CORRECTION_FACTOR 3.0 - UNION_TYPE packed3; packed3.d = VEC_SET1_VAL(TRISTATE_CORRECTION_FACTOR); - + UNION_TYPE packed3; packed3.d = VEC_SET1_VAL(3.0); /* compare rs and N */ - //rs = VEC_LDPOPCVT_CHAR((tc->irs+i*AVX_LENGTH)); - //rsN.d = VEC_CMP_EQ(N_packed256, rs); - +#ifndef MUSTAFA + rs = VEC_LDPOPCVT_CHAR((tc->irs+i*AVX_LENGTH)); + rsN.d = VEC_CMP_EQ(N_packed256, rs); +#endif distm = distm1D[i]; - _1_distm = VEC_SUB(packed1.d, distm); -#ifndef DO_NOT_USE_TRISTATE_CORRECTION - distm = VEC_DIV(distm, packed3.d); -#endif - + _1_distm = VEC_SUB(packed1.d, distm); + + #ifdef KARTHIK + distm = VEC_DIV(distm, packed3.d); + #endif /* initialize M_t_2, M_t_1, X_t_2, X_t_1, Y_t_2, Y_t_1 */ M_t_2.d = VEC_SET1_VAL(zero); X_t_2.d = VEC_SET1_VAL(zero); @@ -234,7 +243,7 @@ template inline void GEN_INTRINSIC(stripINITIALIZATION, PRECISION) -inline _256_TYPE GEN_INTRINSIC(computeDISTM, PRECISION)(int d, int COLS, testcase * tc, HAP_TYPE &hap, _256_TYPE rs, UNION_TYPE rsN, _256_TYPE N_packed256, +inline _256_TYPE GEN_INTRINSIC(GEN_INTRINSIC(computeDISTM, SIMD_TYPE), PRECISION)(int d, int COLS, testcase * tc, HAP_TYPE &hap, _256_TYPE rs, UNION_TYPE rsN, _256_TYPE N_packed256, _256_TYPE distm, _256_TYPE _1_distm) { UNION_TYPE hapN, rshap; @@ -263,12 +272,21 @@ inline _256_TYPE GEN_INTRINSIC(computeDISTM, PRECISION)(int d, int COLS, testcas } -inline void GEN_INTRINSIC(computeMXY, PRECISION)(UNION_TYPE &M_t, UNION_TYPE &X_t, UNION_TYPE &Y_t, UNION_TYPE &M_t_y, +inline void GEN_INTRINSIC(GEN_INTRINSIC(computeMXY, SIMD_TYPE), PRECISION)(UNION_TYPE &M_t, UNION_TYPE &X_t, UNION_TYPE &Y_t, UNION_TYPE &M_t_y, UNION_TYPE M_t_2, UNION_TYPE X_t_2, UNION_TYPE Y_t_2, UNION_TYPE M_t_1, UNION_TYPE X_t_1, UNION_TYPE M_t_1_y, UNION_TYPE Y_t_1, _256_TYPE pMM, _256_TYPE pGAPM, _256_TYPE pMX, _256_TYPE pXX, _256_TYPE pMY, _256_TYPE pYY, _256_TYPE distmSel) { /* Compute M_t <= distm * (p_MM*M_t_2 + p_GAPM*X_t_2 + p_GAPM*Y_t_2) */ M_t.d = VEC_MUL(VEC_ADD(VEC_ADD(VEC_MUL(M_t_2.d, pMM), VEC_MUL(X_t_2.d, pGAPM)), VEC_MUL(Y_t_2.d, pGAPM)), distmSel); + +#ifdef DEBUG + double *temp1 = (double *)(&pGAPM); + double *temp2 = (double *)(&pMM); + double *temp3 = (double *)(&distmSel); + printf("%f\n%f\n%f\n%f\n%f\n%f\n", temp1[0], temp1[1], temp2[0], temp2[1], temp3[0], temp3[1]); + //printf("%f\n%f\n%f\n%f\n", X_t_2.f[0], X_t_2.f[1], Y_t_2.f[0], Y_t_2.f[1]); + printf("%f\n%f\n----------------------------------------------------------------------------\n", M_t.f[0], M_t.f[1]); +#endif M_t_y = M_t; /* Compute X_t */ @@ -278,7 +296,7 @@ inline void GEN_INTRINSIC(computeMXY, PRECISION)(UNION_TYPE &M_t, UNION_TYPE &X_ Y_t.d = VEC_ADD(VEC_MUL(M_t_1_y.d, pMY) , VEC_MUL(Y_t_1.d, pYY)); } -template NUMBER GEN_INTRINSIC(compute_full_prob_avx, PRECISION) (testcase *tc, NUMBER *before_last_log = NULL) +template NUMBER GEN_INTRINSIC(GEN_INTRINSIC(compute_full_prob_,SIMD_TYPE), PRECISION) (testcase *tc, NUMBER *before_last_log = NULL) { _256_TYPE p_MM [MAVX_COUNT], p_GAPM [MAVX_COUNT], p_MX [MAVX_COUNT]; _256_TYPE p_XX [MAVX_COUNT], p_MY [MAVX_COUNT], p_YY [MAVX_COUNT]; @@ -306,54 +324,51 @@ template NUMBER GEN_INTRINSIC(compute_full_prob_avx, PRECISION) (t int remainingRows = (ROWS-1) % AVX_LENGTH; int strip_cnt = ((ROWS-1) / AVX_LENGTH) + (remainingRows!=0); +#ifdef MUSTAFA const int maskBitCnt = MAIN_TYPE_SIZE ; const int numMaskVecs = (COLS+ROWS+maskBitCnt-1)/maskBitCnt ; // ceil function MASK_TYPE maskArr[numMaskVecs][NUM_DISTINCT_CHARS] ; - GEN_INTRINSIC(precompute_masks_, PRECISION)(*tc, COLS, numMaskVecs, maskArr) ; + GEN_INTRINSIC(GEN_INTRINSIC(precompute_masks_,SIMD_TYPE), PRECISION)(*tc, COLS, numMaskVecs, maskArr) ; char rsArr[AVX_LENGTH] ; MASK_TYPE lastMaskShiftOut[AVX_LENGTH] ; - - GEN_INTRINSIC(initializeVectors, PRECISION)(ROWS, COLS, shiftOutM, shiftOutX, shiftOutY, +#endif + GEN_INTRINSIC(GEN_INTRINSIC(initializeVectors,SIMD_TYPE), PRECISION)(ROWS, COLS, shiftOutM, shiftOutX, shiftOutY, ctx, tc, p_MM, p_GAPM, p_MX, p_XX, p_MY, p_YY, distm1D); //for (int __ii=0; __ii < 10; ++__ii) for (int i=0;i NUMBER GEN_INTRINSIC(compute_full_prob_avx, PRECISION) (t int i = strip_cnt-1; { //STRIP_INITIALIZATION - GEN_INTRINSIC(stripINITIALIZATION, PRECISION)(i, ctx, tc, pGAPM, pMM, pMX, pXX, pMY, pYY, rs.d, rsN, distm, _1_distm, distm1D, N_packed256, p_MM , p_GAPM , + GEN_INTRINSIC(GEN_INTRINSIC(stripINITIALIZATION,SIMD_TYPE), PRECISION)(i, ctx, tc, pGAPM, pMM, pMX, pXX, pMY, pYY, rs.d, rsN, distm, _1_distm, distm1D, N_packed256, p_MM , p_GAPM , p_MX, p_XX , p_MY, p_YY, M_t_2, X_t_2, M_t_1, X_t_1, Y_t_2, Y_t_1, M_t_1_y, shiftOutX, shiftOutM); if (remainingRows==0) remainingRows=AVX_LENGTH; - - GEN_INTRINSIC(init_masks_for_row_, PRECISION)(*tc, rsArr, lastMaskShiftOut, - i*AVX_LENGTH+1, remainingRows) ; - +#ifdef MUSTAFA + GEN_INTRINSIC(GEN_INTRINSIC(init_masks_for_row_,SIMD_TYPE), PRECISION)(*tc, rsArr, lastMaskShiftOut, i*AVX_LENGTH+1, remainingRows) ; +#endif _256_TYPE sumM, sumX; sumM = VEC_SET1_VAL(zero); sumX = VEC_SET1_VAL(zero); @@ -382,24 +396,25 @@ template NUMBER GEN_INTRINSIC(compute_full_prob_avx, PRECISION) (t for (int d=1;d #include "headers.h" -#include -#include -#include #include "template.h" +#include "vector_defs.h" -#include "define-float.h" -#include "shift_template.c" -#include "avx_function_prototypes.h" -#include "define-double.h" -#include "shift_template.c" -#include "avx_function_prototypes.h" +#define BATCH_SIZE 10 +#define RUN_HYBRID -#define BATCH_SIZE 10000 -//#define RUN_HYBRID - -//uint8_t ConvertChar::conversionTable[255] ; int thread_level_parallelism_enabled = false ; double getCurrClk() { @@ -26,11 +15,45 @@ double getCurrClk() { return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; } +void print128b_F(__m128 x) +{ + float *p = (float *)(&x); + for (int i=3;i>=0;i--) + printf("%f ", *(p+i)); + printf("\n"); +} + +void print128b_D(__m128d x) +{ + double *p = (double *)(&x); + for (int i=1;i>=0;i--) + printf("%f ", *(p+i)); + printf("\n"); +} int main() { - - testcase tc[BATCH_SIZE]; +/* + IF_128f x; + x.f = _mm_set_ps(1.0, 2.0, 3.0, 4.0); + IF_32 shiftIn, shiftOut; + shiftIn.f = 5.0f; + print128b_F(x.f); + GEN_INTRINSIC(_vector_shift, s)(x, shiftIn, shiftOut); + print128b_F(x.f); + + IF_128d y; + y.f = _mm_set_pd(10.0, 11.0); + IF_64 shiftInd, shiftOutd; + shiftInd.f = 12.0; + print128b_D(y.f); + GEN_INTRINSIC(_vector_shift, d)(y, shiftInd, shiftOutd); + print128b_D(y.f); + + exit(0); +*/ + + testcase tc[BATCH_SIZE]; float result[BATCH_SIZE], result_avxf; double result_avxd; //struct timeval start, end; @@ -41,12 +64,12 @@ int main() // Need to call it once to initialize the static array ConvertChar::init() ; + - - char* ompEnvVar = getenv("OMP_NUM_THREADS") ; - if (ompEnvVar != NULL && ompEnvVar != "" && ompEnvVar != "1" ) { - thread_level_parallelism_enabled = true ; - } +// char* ompEnvVar = getenv("OMP_NUM_THREADS") ; +// if (ompEnvVar != NULL && ompEnvVar != "" && ompEnvVar != "1" ) { +// thread_level_parallelism_enabled = true ; +// } bool noMoreData = false; int count =0; @@ -56,7 +79,7 @@ int main() lastClk = getCurrClk() ; for (int b=0;b(&tc[b]); + result_avxf = GEN_INTRINSIC(GEN_INTRINSIC(compute_full_prob_, SIMD_TYPE), s)(&tc[b]); #ifdef RUN_HYBRID #define MIN_ACCEPTED 1e-28f if (result_avxf < MIN_ACCEPTED) { + //printf("**************** RUNNING DOUBLE ******************\n"); count++; - result_avxd = GEN_INTRINSIC(compute_full_prob_avx, d)(&tc[b]); + result_avxd = GEN_INTRINSIC(GEN_INTRINSIC(compute_full_prob_, SIMD_TYPE), d)(&tc[b]); result[b] = log10(result_avxd) - log10(ldexp(1.0, 1020.f)); } else diff --git a/PairHMM_JNI/run.sh b/PairHMM_JNI/run.sh index 495a3761b..2bc45efad 100755 --- a/PairHMM_JNI/run.sh +++ b/PairHMM_JNI/run.sh @@ -2,18 +2,21 @@ rm -f *.txt export GSA_ROOT_DIR=/home/karthikg/broad/gsa-unstable #-Djava.library.path is needed if you are using JNI_LOGLESS_CACHING, else not needed -java -Djava.library.path=${GSA_ROOT_DIR}/PairHMM_JNI -jar ${GSA_ROOT_DIR}/dist/GenomeAnalysisTK.jar -T HaplotypeCaller \ +java -Djava.library.path=${GSA_ROOT_DIR}/PairHMM_JNI -jar $GSA_ROOT_DIR/dist/GenomeAnalysisTK.jar -T HaplotypeCaller \ -R /opt/Genomics/ohsu/dnapipeline/humanrefgenome/human_g1k_v37.fasta \ --I /data/broad/samples/joint_variant_calling/NA12878_low_coverage_alignment/NA12878.chrom11.ILLUMINA.bwa.CEU.low_coverage.20121211.bam \ +-I /data/broad/samples/joint_variant_calling/NA12878_high_coverage_alignment/NA12878.mapped.ILLUMINA.bwa.CEU.high_coverage_pcr_free.20130906.bam \ --dbsnp /data/broad/samples/joint_variant_calling/dbSNP/00-All.vcf \ -stand_call_conf 50.0 \ -stand_emit_conf 10.0 \ --pair_hmm_implementation JNI_LOGLESS_CACHING \ +-XL unmapped \ -o output.raw.snps.indels.vcf #--pair_hmm_implementation JNI_LOGLESS_CACHING \ #-I /data/simulated/sim1M_pairs_final.bam \ #-I /data/broad/samples/joint_variant_calling/NA12878_low_coverage_alignment/NA12878.chrom11.ILLUMINA.bwa.CEU.low_coverage.20121211.bam \ +#-I /data/broad/samples/joint_variant_calling/NA12878_high_coverage_alignment/NA12878.mapped.ILLUMINA.bwa.CEU.high_coverage_pcr_free.20130906.bam \ +#-R /data/broad/samples/joint_variant_calling/broad_reference/Homo_sapiens_assembly18.fasta \ #-R /data/broad/samples/joint_variant_calling/broad_reference/Homo_sapiens_assembly19.fasta \ #-R /data/broad/samples/joint_variant_calling/broad_reference/ucsc.hg19.fasta \ #-R /opt/Genomics/ohsu/dnapipeline/humanrefgenome/human_g1k_v37.fasta \ diff --git a/PairHMM_JNI/shift_template.c b/PairHMM_JNI/shift_template.c index 90160dcb3..db4a4f6f7 100644 --- a/PairHMM_JNI/shift_template.c +++ b/PairHMM_JNI/shift_template.c @@ -1,9 +1,8 @@ #ifdef PRECISION -#include "template.h" +#ifdef SIMD_TYPE_AVX - -inline void GEN_INTRINSIC(_vector_shift, PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn, MAIN_TYPE &shiftOut) +inline void GEN_INTRINSIC(GEN_INTRINSIC(_vector_shift,SIMD_TYPE), PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn, MAIN_TYPE &shiftOut) { IF_128 xlow , xhigh; @@ -33,7 +32,8 @@ inline void GEN_INTRINSIC(_vector_shift, PRECISION) (UNION_TYPE &x, MAIN_TYPE sh x.d = VEC_INSERT_VAL(x.d, xhigh.f, 1); } -inline void GEN_INTRINSIC(_vector_shift_last, PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn) + +inline void GEN_INTRINSIC(GEN_INTRINSIC(_vector_shift_last, SIMD_TYPE), PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn) { IF_128 xlow , xhigh; @@ -44,10 +44,6 @@ inline void GEN_INTRINSIC(_vector_shift_last, PRECISION) (UNION_TYPE &x, MAIN_TY /* extract xlow[3] */ IF_128 shiftOutL128; shiftOutL128.i = _mm_srli_si128(xlow.i, SHIFT_CONST1); - /* extract xhigh[3] */ -// IF_MAIN_TYPE shiftOutH; -// shiftOutH.i = VEC_EXTRACT_UNIT(xhigh.i, SHIFT_CONST2); -// shiftOut = shiftOutH.f; /* shift xlow */ xlow.i = _mm_slli_si128 (xlow.i, SHIFT_CONST3); /* shift xhigh */ @@ -63,6 +59,32 @@ inline void GEN_INTRINSIC(_vector_shift_last, PRECISION) (UNION_TYPE &x, MAIN_TY x.d = VEC_INSERT_VAL(x.d, xhigh.f, 1); } +#endif + +#ifdef SIMD_TYPE_SSE + +inline void GEN_INTRINSIC(GEN_INTRINSIC(_vector_shift, SIMD_TYPE), PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn, MAIN_TYPE &shiftOut) +{ + IF_MAIN_TYPE tempIn, tempOut; + tempIn.f = shiftIn; + /* extratc H */ + tempOut.i = VEC_EXTRACT_UNIT(x.i, SHIFT_CONST1); + shiftOut = tempOut.f; + /* shift */ + x.i = _mm_slli_si128(x.i, SHIFT_CONST2); + /* insert L */ + x.i = VEC_INSERT_UNIT(x.i , tempIn.i, SHIFT_CONST3); +} + +inline void GEN_INTRINSIC(GEN_INTRINSIC(_vector_shift_last, SIMD_TYPE), PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn) +{ + IF_MAIN_TYPE temp; temp.f = shiftIn; + /* shift */ + x.i = _mm_slli_si128(x.i, SHIFT_CONST2); + /* insert L */ + x.i = VEC_INSERT_UNIT(x.i , temp.i, SHIFT_CONST3); +} #endif +#endif diff --git a/PairHMM_JNI/sse_function_instantiations.cc b/PairHMM_JNI/sse_function_instantiations.cc new file mode 100644 index 000000000..38686ada2 --- /dev/null +++ b/PairHMM_JNI/sse_function_instantiations.cc @@ -0,0 +1,18 @@ +#include "template.h" + +#undef SIMD_TYPE +#undef SIMD_TYPE_AVX + +#define SIMD_TYPE sse +#define SIMD_TYPE_SSE + +#include "define-sse-float.h" +#include "shift_template.c" +#include "pairhmm-template-kernel.cc" + +#include "define-sse-double.h" +#include "shift_template.c" +#include "pairhmm-template-kernel.cc" + +template double compute_full_prob_ssed(testcase* tc, double* nextlog); +template float compute_full_prob_sses(testcase* tc, float* nextlog); diff --git a/PairHMM_JNI/template.h b/PairHMM_JNI/template.h index 52a4e4650..0ba4f843d 100644 --- a/PairHMM_JNI/template.h +++ b/PairHMM_JNI/template.h @@ -25,12 +25,25 @@ typedef union __attribute__((aligned(32))) { ALIGNED __m256i ALIGNED i; } ALIGNED mix_F ALIGNED; +typedef union __attribute__((aligned(32))) { + ALIGNED __m128 ALIGNED d; + ALIGNED __m64 ALIGNED s[2]; + ALIGNED float ALIGNED f[4]; + ALIGNED __m128i ALIGNED i; +} ALIGNED mix_F128 ALIGNED; + typedef union ALIGNED { __m128i vec ; __m128 vecf ; uint32_t masks[4] ; } MaskVec_F ; +typedef union ALIGNED { + __m64 vec ; + __m64 vecf ; + uint32_t masks[2] ; +} MaskVec_F128 ; + typedef union ALIGNED { ALIGNED __m128i ALIGNED i; @@ -50,12 +63,25 @@ typedef union __attribute__((aligned(32))) { ALIGNED __m256i ALIGNED i; } ALIGNED mix_D ALIGNED; +typedef union __attribute__((aligned(32))) { + ALIGNED __m128d ALIGNED d; + ALIGNED __m64 ALIGNED s[2]; + ALIGNED double ALIGNED f[2]; + ALIGNED __m128i ALIGNED i; +} ALIGNED mix_D128 ALIGNED; + typedef union ALIGNED { __m128i vec ; __m128d vecf ; uint64_t masks[2] ; } MaskVec_D ; +typedef union ALIGNED { + __m64 vec ; + __m64 vecf ; + uint64_t masks[1] ; +} MaskVec_D128 ; + typedef union ALIGNED { ALIGNED __m128i ALIGNED i; @@ -120,7 +146,6 @@ struct Context }; - typedef struct { int rslen, haplen; @@ -131,24 +156,11 @@ typedef struct int *irs; } testcase; - -template -std::string to_string(T obj) -{ - std::stringstream ss; - std::string ret_string; - ss.clear(); - ss << std::scientific << obj; - ss >> ret_string; - ss.clear(); - return ret_string; -} -void debug_dump(std::string filename, std::string s, bool to_append, bool add_newline=true); - int normalize(char c); -int read_testcase(testcase *tc, FILE* ifp); -int read_mod_testcase(std::ifstream& fptr, testcase* tc, bool reformat=false); +int read_testcase(testcase *tc, FILE* ifp=0); + +#define MIN_ACCEPTED 1e-28f #define NUM_DISTINCT_CHARS 5 #define AMBIG_CHAR 4 @@ -176,7 +188,6 @@ public: }; - #endif diff --git a/PairHMM_JNI/utils.cc b/PairHMM_JNI/utils.cc index 5b91fc7e9..94e2144c0 100644 --- a/PairHMM_JNI/utils.cc +++ b/PairHMM_JNI/utils.cc @@ -1,5 +1,7 @@ #include "headers.h" #include "template.h" +#include "utils.h" +#include "vector_defs.h" uint8_t ConvertChar::conversionTable[255]; float (*g_compute_full_prob_float)(testcase *tc, float* before_last_log) = 0; @@ -31,6 +33,30 @@ bool is_sse42_supported() return ((ecx >> 20)&1) == 1; } +void initialize_function_pointers() +{ + //if(is_avx_supported()) + if(false) + { + cout << "Using AVX accelerated implementation of PairHMM\n"; + g_compute_full_prob_float = compute_full_prob_avxs; + g_compute_full_prob_double = compute_full_prob_avxd; + } + else + if(is_sse42_supported()) + { + cout << "Using SSE4.2 accelerated implementation of PairHMM\n"; + g_compute_full_prob_float = compute_full_prob_sses; + g_compute_full_prob_double = compute_full_prob_ssed; + } + else + { + cout << "Using un-vectorized C++ implementation of PairHMM\n"; + g_compute_full_prob_float = compute_full_prob; + g_compute_full_prob_double = compute_full_prob; + } +} + int normalize(char c) { return ((int) (c - 33)); @@ -214,12 +240,3 @@ int read_mod_testcase(ifstream& fptr, testcase* tc, bool reformat) return tokens.size(); } -void debug_dump(string filename, string s, bool to_append, bool add_newline) -{ - ofstream fptr; - fptr.open(filename.c_str(), to_append ? ofstream::app : ofstream::out); - fptr << s; - if(add_newline) - fptr << "\n"; - fptr.close(); -} diff --git a/PairHMM_JNI/utils.h b/PairHMM_JNI/utils.h index 8c244333e..8da8269b8 100644 --- a/PairHMM_JNI/utils.h +++ b/PairHMM_JNI/utils.h @@ -1,7 +1,22 @@ #ifndef PAIRHMM_UTIL_H #define PAIRHMM_UTIL_H -#define MIN_ACCEPTED 1e-28f + +template +std::string to_string(T obj) +{ + std::stringstream ss; + std::string ret_string; + ss.clear(); + ss << std::scientific << obj; + ss >> ret_string; + ss.clear(); + return ret_string; +} +void debug_dump(std::string filename, std::string s, bool to_append, bool add_newline=true); + +int read_mod_testcase(std::ifstream& fptr, testcase* tc, bool reformat=false); + bool is_avx_supported(); bool is_sse42_supported(); extern float (*g_compute_full_prob_float)(testcase *tc, float *before_last_log); @@ -9,5 +24,5 @@ extern double (*g_compute_full_prob_double)(testcase *tc, double* before_last_lo void debug_dump(std::string filename, std::string s, bool to_append, bool add_newline); template NUMBER compute_full_prob(testcase *tc, NUMBER *before_last_log=0); - +void initialize_function_pointers(); #endif diff --git a/PairHMM_JNI/vector_defs.h b/PairHMM_JNI/vector_defs.h new file mode 100644 index 000000000..c89f7f932 --- /dev/null +++ b/PairHMM_JNI/vector_defs.h @@ -0,0 +1,26 @@ +#undef SIMD_TYPE +#undef SIMD_TYPE_AVX +#undef SIMD_TYPE_SSE + +#define SIMD_TYPE avx +#define SIMD_TYPE_AVX + +#include "define-float.h" +#include "vector_function_prototypes.h" + +#include "define-double.h" +#include "vector_function_prototypes.h" + +#undef SIMD_TYPE +#undef SIMD_TYPE_AVX + +#define SIMD_TYPE sse +#define SIMD_TYPE_SSE + +#include "define-sse-float.h" +#include "vector_function_prototypes.h" + +#include "define-sse-double.h" +#include "vector_function_prototypes.h" + + diff --git a/PairHMM_JNI/vector_function_prototypes.h b/PairHMM_JNI/vector_function_prototypes.h new file mode 100644 index 000000000..2593986a6 --- /dev/null +++ b/PairHMM_JNI/vector_function_prototypes.h @@ -0,0 +1,19 @@ +void GEN_INTRINSIC(GEN_INTRINSIC(_vector_shift,SIMD_TYPE), PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn, MAIN_TYPE &shiftOut); +void GEN_INTRINSIC(GEN_INTRINSIC(_vector_shift_last,SIMD_TYPE), PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn); +void GEN_INTRINSIC(GEN_INTRINSIC(precompute_masks_,SIMD_TYPE), PRECISION)(const testcase& tc, int COLS, int numMaskVecs, MASK_TYPE (*maskArr)[NUM_DISTINCT_CHARS]); +void GEN_INTRINSIC(GEN_INTRINSIC(init_masks_for_row_,SIMD_TYPE), PRECISION)(const testcase& tc, char* rsArr, MASK_TYPE* lastMaskShiftOut, int beginRowIndex, int numRowsToProcess); +void GEN_INTRINSIC(GEN_INTRINSIC(update_masks_for_cols_,SIMD_TYPE), PRECISION)(int maskIndex, MASK_VEC& currMaskVecLow, MASK_VEC& currMaskVecHigh, MASK_TYPE (*maskArr) [NUM_DISTINCT_CHARS], char* rsArr, MASK_TYPE* lastMaskShiftOut, MASK_TYPE maskBitCnt); +void GEN_INTRINSIC(GEN_INTRINSIC(computeDistVec,SIMD_TYPE), PRECISION) (MASK_VEC& currMaskVecLow, MASK_VEC& currMaskVecHigh, _256_TYPE& distm, _256_TYPE& _1_distm, _256_TYPE& distmChosen); +template void GEN_INTRINSIC(GEN_INTRINSIC(initializeVectors,SIMD_TYPE), PRECISION)(int ROWS, int COLS, NUMBER* shiftOutM, NUMBER *shiftOutX, NUMBER *shiftOutY, Context ctx, testcase *tc, _256_TYPE *p_MM, _256_TYPE *p_GAPM, _256_TYPE *p_MX, _256_TYPE *p_XX, _256_TYPE *p_MY, _256_TYPE *p_YY, _256_TYPE *distm1D); +template void GEN_INTRINSIC(GEN_INTRINSIC(stripINITIALIZATION,SIMD_TYPE), PRECISION)( + int stripIdx, Context ctx, testcase *tc, _256_TYPE &pGAPM, _256_TYPE &pMM, _256_TYPE &pMX, _256_TYPE &pXX, _256_TYPE &pMY, _256_TYPE &pYY, + _256_TYPE &rs, UNION_TYPE &rsN, _256_TYPE &distm, _256_TYPE &_1_distm, _256_TYPE *distm1D, _256_TYPE N_packed256, _256_TYPE *p_MM , _256_TYPE *p_GAPM , + _256_TYPE *p_MX, _256_TYPE *p_XX , _256_TYPE *p_MY, _256_TYPE *p_YY, UNION_TYPE &M_t_2, UNION_TYPE &X_t_2, UNION_TYPE &M_t_1, UNION_TYPE &X_t_1, + UNION_TYPE &Y_t_2, UNION_TYPE &Y_t_1, UNION_TYPE &M_t_1_y, NUMBER* shiftOutX, NUMBER* shiftOutM); +_256_TYPE GEN_INTRINSIC(GEN_INTRINSIC(computeDISTM,SIMD_TYPE), PRECISION)(int d, int COLS, testcase * tc, HAP_TYPE &hap, _256_TYPE rs, UNION_TYPE rsN, _256_TYPE N_packed256, + _256_TYPE distm, _256_TYPE _1_distm); +void GEN_INTRINSIC(GEN_INTRINSIC(computeMXY,SIMD_TYPE), PRECISION)(UNION_TYPE &M_t, UNION_TYPE &X_t, UNION_TYPE &Y_t, UNION_TYPE &M_t_y, + UNION_TYPE M_t_2, UNION_TYPE X_t_2, UNION_TYPE Y_t_2, UNION_TYPE M_t_1, UNION_TYPE X_t_1, UNION_TYPE M_t_1_y, UNION_TYPE Y_t_1, + _256_TYPE pMM, _256_TYPE pGAPM, _256_TYPE pMX, _256_TYPE pXX, _256_TYPE pMY, _256_TYPE pYY, _256_TYPE distmSel); +template NUMBER GEN_INTRINSIC(GEN_INTRINSIC(compute_full_prob_,SIMD_TYPE), PRECISION) (testcase *tc, NUMBER *before_last_log = NULL); + diff --git a/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/HaplotypeCaller.java b/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/HaplotypeCaller.java index 82015d153..b785b8d21 100644 --- a/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/HaplotypeCaller.java +++ b/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/HaplotypeCaller.java @@ -1086,7 +1086,7 @@ public class HaplotypeCaller extends ActiveRegionWalker, In referenceConfidenceModel.close(); //TODO remove the need to call close here for debugging, the likelihood output stream should be managed //TODO (open & close) at the walker, not the engine. - //likelihoodCalculationEngine.close(); + likelihoodCalculationEngine.close(); logger.info("Ran local assembly on " + result + " active regions"); } diff --git a/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/LikelihoodCalculationEngine.java b/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/LikelihoodCalculationEngine.java index 0626f2268..04e64186f 100644 --- a/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/LikelihoodCalculationEngine.java +++ b/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/LikelihoodCalculationEngine.java @@ -89,4 +89,6 @@ public interface LikelihoodCalculationEngine { */ public Map computeReadLikelihoods(AssemblyResultSet assemblyResultSet, Map> perSampleReadList); + + public void close(); } diff --git a/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/PairHMMLikelihoodCalculationEngine.java b/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/PairHMMLikelihoodCalculationEngine.java index 0a82e1997..5ab49b531 100644 --- a/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/PairHMMLikelihoodCalculationEngine.java +++ b/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/PairHMMLikelihoodCalculationEngine.java @@ -165,8 +165,10 @@ public class PairHMMLikelihoodCalculationEngine implements LikelihoodCalculation } } + @Override public void close() { if ( likelihoodsStream != null ) likelihoodsStream.close(); + pairHMMThreadLocal.get().close(); } private void writeDebugLikelihoods(final GATKSAMRecord processedRead, final Haplotype haplotype, final double log10l){ diff --git a/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java b/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java index a80800f82..ca3db5ed3 100644 --- a/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java +++ b/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java @@ -66,7 +66,8 @@ import java.util.Map; */ public class JNILoglessPairHMM extends LoglessPairHMM { - private static final boolean debug = true; //simulates ifdef + private static final boolean debug = false; //simulates ifdef + private static final boolean verify = debug || false; //simulates ifdef private static final boolean debug0_1 = false; //simulates ifdef private static final boolean debug1 = false; //simulates ifdef private static final boolean debug2 = false; @@ -90,6 +91,7 @@ public class JNILoglessPairHMM extends LoglessPairHMM { } private native void jniGlobalInit(Class readDataHolderClass, Class haplotypeDataHolderClass); + private native void jniClose(); private static boolean isJNILoglessPairHMMLibraryLoaded = false; @@ -104,6 +106,13 @@ public class JNILoglessPairHMM extends LoglessPairHMM { } } + @Override + public void close() + { + jniClose(); + debugClose(); + } + //Used to test parts of the compute kernel separately private native void jniInitialize(final int readMaxLength, final int haplotypeMaxLength); private native static void jniInitializeProbabilities(final double[][] transition, final byte[] insertionGOP, final byte[] deletionGOP, final byte[] overallGCP); @@ -126,7 +135,7 @@ public class JNILoglessPairHMM extends LoglessPairHMM { @Override public void initialize(final int readMaxLength, final int haplotypeMaxLength) { - if(debug) + if(verify) super.initialize(readMaxLength, haplotypeMaxLength); if(debug3) { @@ -150,14 +159,14 @@ public class JNILoglessPairHMM extends LoglessPairHMM { public PerReadAlleleLikelihoodMap computeLikelihoods(final List reads, final Map alleleHaplotypeMap, final Map GCPArrayMap) { // (re)initialize the pairHMM only if necessary - final int readMaxLength = debug ? findMaxReadLength(reads) : 0; - final int haplotypeMaxLength = debug ? findMaxHaplotypeLength(alleleHaplotypeMap) : 0; - if(debug) + final int readMaxLength = verify ? findMaxReadLength(reads) : 0; + final int haplotypeMaxLength = verify ? findMaxHaplotypeLength(alleleHaplotypeMap) : 0; + if(verify) { if (!initialized || readMaxLength > maxReadLength || haplotypeMaxLength > maxHaplotypeLength) { initialize(readMaxLength, haplotypeMaxLength); } if ( ! initialized ) - throw new IllegalStateException("Must call initialize before calling jniComputeLikelihoods in debug mode"); + throw new IllegalStateException("Must call initialize before calling jniComputeLikelihoods in debug/verify mode"); } int readListSize = reads.size(); int alleleHaplotypeMapSize = alleleHaplotypeMap.size(); @@ -221,7 +230,7 @@ public class JNILoglessPairHMM extends LoglessPairHMM { likelihoodMap.add(read, currEntry.getKey(), likelihoodArray[idx]); ++idx; } - if(debug) + if(verify) { //to compare values likelihoodMap = super.computeLikelihoods(reads, alleleHaplotypeMap, GCPArrayMap); @@ -273,6 +282,7 @@ public class JNILoglessPairHMM extends LoglessPairHMM { } ++numComputeLikelihoodCalls; //if(numComputeLikelihoodCalls == 5) + //jniClose(); //System.exit(0); return likelihoodMap; } @@ -299,11 +309,11 @@ public class JNILoglessPairHMM extends LoglessPairHMM { //} //System.out.println("#### END STACK TRACE ####"); // - if(debug1) - jniSubComputeReadLikelihoodGivenHaplotypeLog10(readBases.length, haplotypeBases.length, - readBases, haplotypeBases, readQuals, - insertionGOP, deletionGOP, overallGCP, - hapStartIndex); + if(debug1) + jniSubComputeReadLikelihoodGivenHaplotypeLog10(readBases.length, haplotypeBases.length, + readBases, haplotypeBases, readQuals, + insertionGOP, deletionGOP, overallGCP, + hapStartIndex); boolean doInitialization = (previousHaplotypeBases == null || previousHaplotypeBases.length != haplotypeBases.length); if (doInitialization) { @@ -358,8 +368,8 @@ public class JNILoglessPairHMM extends LoglessPairHMM { ++numCalls; //if(numCalls == 100) //{ - //debugClose(); - //System.exit(0); + //debugClose(); + //System.exit(0); //} return Math.log10(finalSumProbabilities) - INITIAL_CONDITION_LOG10; } diff --git a/public/java/src/org/broadinstitute/sting/utils/pairhmm/PairHMM.java b/public/java/src/org/broadinstitute/sting/utils/pairhmm/PairHMM.java index 3b4498776..7b09fe047 100644 --- a/public/java/src/org/broadinstitute/sting/utils/pairhmm/PairHMM.java +++ b/public/java/src/org/broadinstitute/sting/utils/pairhmm/PairHMM.java @@ -280,4 +280,7 @@ public abstract class PairHMM { return Math.min(haplotype1.length, haplotype2.length); } + + //Called at the end of all HC calls + public void close() { ; } } From f614d7b0d8e7b71d79655f510cd404f1672e6161 Mon Sep 17 00:00:00 2001 From: Karthik Gururaj Date: Mon, 20 Jan 2014 08:51:53 -0800 Subject: [PATCH 03/10] 1. Enabled OpenMP 2. Enabled AVX - earlier commit had disabled AVX --- PairHMM_JNI/Makefile | 2 +- PairHMM_JNI/libJNILoglessPairHMM.so | Bin 0 -> 114104 bytes PairHMM_JNI/pairhmm-1-base.cc | 39 +++++++++++++++++++++++++--- PairHMM_JNI/utils.cc | 3 +-- 4 files changed, 38 insertions(+), 6 deletions(-) create mode 100755 PairHMM_JNI/libJNILoglessPairHMM.so diff --git a/PairHMM_JNI/Makefile b/PairHMM_JNI/Makefile index b86958a6c..e966caa90 100644 --- a/PairHMM_JNI/Makefile +++ b/PairHMM_JNI/Makefile @@ -1,4 +1,4 @@ -#OMPCFLAGS=-fopenmp +OMPCFLAGS=-fopenmp #OMPLDFLAGS=-lgomp #CFLAGS=-O2 -std=c++11 -W -Wall -march=corei7-avx -Wa,-q -pedantic $(OMPCFLAGS) -Wno-unknown-pragmas diff --git a/PairHMM_JNI/libJNILoglessPairHMM.so b/PairHMM_JNI/libJNILoglessPairHMM.so new file mode 100755 index 0000000000000000000000000000000000000000..7a771922fbd614ed3955980b46f605347db8f67a GIT binary patch literal 114104 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 KOxM<&^8Wx= 3 && string(argv[2]) == "1") use_old_read_testcase = true; + unsigned chunk_size = 100; + if(argc >= 4) + chunk_size = strtol(argv[3],0,10); + initialize_function_pointers(); @@ -45,12 +55,24 @@ int main(int argc, char** argv) assert(ifptr.is_open()); } + vector tc_vector; + tc_vector.clear(); testcase tc; while(1) { int break_value = use_old_read_testcase ? read_testcase(&tc, fptr) : read_mod_testcase(ifptr,&tc,true); if(break_value < 0) break; + tc_vector.push_back(tc); + } + vector results_vec; + results_vec.clear(); + results_vec.resize(tc_vector.size()); + double start_time = getCurrClk(); +#pragma omp parallel for schedule(dynamic,chunk_size) num_threads(12) + for(unsigned i=0;i(&tc); baseline_result = log10(baseline_result) - log10(ldexp(1.0, 1020.0)); - cout << std::scientific << baseline_result << " "< 1e-5 && rel_error > 1e-5) + cout << std::scientific << baseline_result << " "<; From 28891117e23522aa7e9f10b2e9bed973e46068d4 Mon Sep 17 00:00:00 2001 From: Karthik Gururaj Date: Mon, 20 Jan 2014 11:07:44 -0800 Subject: [PATCH 04/10] Fixed bug in JNI interface release_array Disabled OpenMP --- PairHMM_JNI/Makefile | 3 +-- PairHMM_JNI/libJNILoglessPairHMM.so | Bin 114104 -> 102085 bytes ...e_sting_utils_pairhmm_JNILoglessPairHMM.cc | 4 ++-- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/PairHMM_JNI/Makefile b/PairHMM_JNI/Makefile index e966caa90..f6dfbb1f0 100644 --- a/PairHMM_JNI/Makefile +++ b/PairHMM_JNI/Makefile @@ -1,5 +1,4 @@ -OMPCFLAGS=-fopenmp -#OMPLDFLAGS=-lgomp +#OMPCFLAGS=-fopenmp #CFLAGS=-O2 -std=c++11 -W -Wall -march=corei7-avx -Wa,-q -pedantic $(OMPCFLAGS) -Wno-unknown-pragmas #CFLAGS=-O2 -W -Wall -march=corei7 -mfpmath=sse -msse4.2 -pedantic $(OMPCFLAGS) -Wno-unknown-pragmas diff --git a/PairHMM_JNI/libJNILoglessPairHMM.so b/PairHMM_JNI/libJNILoglessPairHMM.so index 7a771922fbd614ed3955980b46f605347db8f67a..053b901cd7d16182b21054593096ab2ad1c2a0b2 100755 GIT binary patch literal 102085 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 KOxM<&^8WxGetPrimitiveArrayCritical #define RELEASE_BYTE_ARRAY_ELEMENTS env->ReleasePrimitiveArrayCritical -#define JNI_RELEASE_MODE JNI_ABORT +#define JNI_RELEASE_MODE 0 #define GET_DOUBLE_ARRAY_ELEMENTS env->GetPrimitiveArrayCritical #define RELEASE_DOUBLE_ARRAY_ELEMENTS env->ReleasePrimitiveArrayCritical @@ -384,7 +384,7 @@ JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPai //Release read arrays first for(int i=readBasesArrayVector.size()-1;i>=0;--i)//note the order - reverse of GET { - for(int j=readBasesArrayVector.size()-1;j>=0;--j) + for(int j=readBasesArrayVector[i].size()-1;j>=0;--j) RELEASE_BYTE_ARRAY_ELEMENTS(readBasesArrayVector[i][j].first, readBasesArrayVector[i][j].second, JNI_RELEASE_MODE); readBasesArrayVector[i].clear(); } From 0170d4f3d5d4f8d182074fb968302c3e5da4163a Mon Sep 17 00:00:00 2001 From: mozdal Date: Tue, 21 Jan 2014 09:30:15 -0800 Subject: [PATCH 05/10] Got rid of the MMX instructions in the SSE version of the code. Handling the mask operations in a class, which is defined for each version of SSE and AVX implementations separately. --- PairHMM_JNI/avx_function_instantiations.cc | 1 + PairHMM_JNI/define-double.h | 30 +++++++++++++++++ PairHMM_JNI/define-float.h | 29 ++++++++++++++++ PairHMM_JNI/define-sse-double.h | 31 +++++++++++++++-- PairHMM_JNI/define-sse-float.h | 30 +++++++++++++++-- PairHMM_JNI/pairhmm-1-base.cc | 39 ++++++++++++++++++++-- PairHMM_JNI/pairhmm-template-kernel.cc | 37 +++++++++----------- PairHMM_JNI/utils.cc | 3 +- PairHMM_JNI/vector_defs.h | 1 + 9 files changed, 169 insertions(+), 32 deletions(-) diff --git a/PairHMM_JNI/avx_function_instantiations.cc b/PairHMM_JNI/avx_function_instantiations.cc index b236ddc8f..4118fc5cf 100644 --- a/PairHMM_JNI/avx_function_instantiations.cc +++ b/PairHMM_JNI/avx_function_instantiations.cc @@ -16,3 +16,4 @@ template double compute_full_prob_avxd(testcase* tc, double* nextlog); template float compute_full_prob_avxs(testcase* tc, float* nextlog); + diff --git a/PairHMM_JNI/define-double.h b/PairHMM_JNI/define-double.h index 79c6b323f..502b919fe 100644 --- a/PairHMM_JNI/define-double.h +++ b/PairHMM_JNI/define-double.h @@ -47,6 +47,7 @@ #undef MASK_ALL_ONES #undef COMPARE_VECS(__v1, __v2) #undef _256_INT_TYPE + #undef BITMASK_VEC #endif #define PRECISION d @@ -156,3 +157,32 @@ } \ } \ } + +class BitMaskVec_double { + + MASK_VEC low_, high_ ; + _256_TYPE combined_ ; + +public: + + inline MASK_TYPE& getLowEntry(int index) { + return low_.masks[index] ; + } + inline MASK_TYPE& getHighEntry(int index) { + return high_.masks[index] ; + } + + inline const _256_TYPE& getCombinedMask() { + VEC_SSE_TO_AVX(low_.vecf, high_.vecf, combined_) ; + + return combined_ ; + } + + inline void shift_left_1bit() { + VEC_SHIFT_LEFT_1BIT(low_.vec) ; + VEC_SHIFT_LEFT_1BIT(high_.vec) ; + } + +} ; + +#define BITMASK_VEC BitMaskVec_double diff --git a/PairHMM_JNI/define-float.h b/PairHMM_JNI/define-float.h index 25ebd1489..3cc57ec38 100644 --- a/PairHMM_JNI/define-float.h +++ b/PairHMM_JNI/define-float.h @@ -47,6 +47,7 @@ #undef MASK_ALL_ONES #undef COMPARE_VECS(__v1, __v2) #undef _256_INT_TYPE + #undef BITMASK_VEC #endif #define PRECISION s @@ -157,3 +158,31 @@ } \ } +class BitMaskVec_float { + + MASK_VEC low_, high_ ; + _256_TYPE combined_ ; + +public: + + inline MASK_TYPE& getLowEntry(int index) { + return low_.masks[index] ; + } + inline MASK_TYPE& getHighEntry(int index) { + return high_.masks[index] ; + } + + inline const _256_TYPE& getCombinedMask() { + VEC_SSE_TO_AVX(low_.vecf, high_.vecf, combined_) ; + + return combined_ ; + } + + inline void shift_left_1bit() { + VEC_SHIFT_LEFT_1BIT(low_.vec) ; + VEC_SHIFT_LEFT_1BIT(high_.vec) ; + } + +} ; + +#define BITMASK_VEC BitMaskVec_float diff --git a/PairHMM_JNI/define-sse-double.h b/PairHMM_JNI/define-sse-double.h index e48325ba9..a30b2e5f5 100644 --- a/PairHMM_JNI/define-sse-double.h +++ b/PairHMM_JNI/define-sse-double.h @@ -47,7 +47,7 @@ #undef MASK_ALL_ONES #undef COMPARE_VECS(__v1, __v2) #undef _256_INT_TYPE - + #undef BITMASK_VEC #endif #define SSE @@ -69,7 +69,7 @@ #define HAP_TYPE __m128i #define MASK_TYPE uint64_t #define MASK_ALL_ONES 0xFFFFFFFFFFFFFFFFL -#define MASK_VEC MaskVec_D128 +#define MASK_VEC MaskVec_D #define VEC_EXTRACT_UNIT(__v1, __im) \ _mm_extract_epi64(__v1, __im) @@ -123,6 +123,31 @@ __vdst = _mm_castsi128_pd(_mm_set_epi64(__vsHigh, __vsLow)) #define VEC_SHIFT_LEFT_1BIT(__vs) \ - __vs = _mm_slli_si64(__vs, 1) + __vs = _mm_slli_epi64(__vs, 1) +class BitMaskVec_sse_double { + + MASK_VEC combined_ ; + +public: + + inline MASK_TYPE& getLowEntry(int index) { + return combined_.masks[index] ; + } + inline MASK_TYPE& getHighEntry(int index) { + return combined_.masks[AVX_LENGTH/2+index] ; + } + + inline const _256_TYPE& getCombinedMask() { + return combined_.vecf ; + } + + inline void shift_left_1bit() { + VEC_SHIFT_LEFT_1BIT(combined_.vec) ; + } + +} ; + +#define BITMASK_VEC BitMaskVec_sse_double + diff --git a/PairHMM_JNI/define-sse-float.h b/PairHMM_JNI/define-sse-float.h index f5758c74a..6612b28e6 100644 --- a/PairHMM_JNI/define-sse-float.h +++ b/PairHMM_JNI/define-sse-float.h @@ -47,7 +47,7 @@ #undef MASK_ALL_ONES #undef COMPARE_VECS(__v1, __v2) #undef _256_INT_TYPE - + #undef BITMASK_VEC #endif #define SSE @@ -69,7 +69,7 @@ #define HAP_TYPE UNION_TYPE #define MASK_TYPE uint32_t #define MASK_ALL_ONES 0xFFFFFFFF -#define MASK_VEC MaskVec_F128 +#define MASK_VEC MaskVec_F #define VEC_EXTRACT_UNIT(__v1, __im) \ _mm_extract_epi32(__v1, __im) @@ -123,5 +123,29 @@ __vdst = _mm_cvtpi32x2_ps(__vsLow, __vsHigh) #define VEC_SHIFT_LEFT_1BIT(__vs) \ - __vs = _mm_slli_pi32(__vs, 1) + __vs = _mm_slli_epi32(__vs, 1) +class BitMaskVec_sse_float { + + MASK_VEC combined_ ; + +public: + + inline MASK_TYPE& getLowEntry(int index) { + return combined_.masks[index] ; + } + inline MASK_TYPE& getHighEntry(int index) { + return combined_.masks[AVX_LENGTH/2+index] ; + } + + inline const _256_TYPE& getCombinedMask() { + return combined_.vecf ; + } + + inline void shift_left_1bit() { + VEC_SHIFT_LEFT_1BIT(combined_.vec) ; + } + +} ; + +#define BITMASK_VEC BitMaskVec_sse_float diff --git a/PairHMM_JNI/pairhmm-1-base.cc b/PairHMM_JNI/pairhmm-1-base.cc index 3030b640e..3941c92de 100644 --- a/PairHMM_JNI/pairhmm-1-base.cc +++ b/PairHMM_JNI/pairhmm-1-base.cc @@ -19,6 +19,12 @@ LoadTimeInitializer g_load_time_initializer; #define BATCH_SIZE 10000 #define RUN_HYBRID +double getCurrClk() { + struct timeval tv ; + gettimeofday(&tv, NULL); + return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; +} + int main(int argc, char** argv) { if(argc < 2) @@ -29,6 +35,10 @@ int main(int argc, char** argv) bool use_old_read_testcase = false; if(argc >= 3 && string(argv[2]) == "1") use_old_read_testcase = true; + unsigned chunk_size = 100; + if(argc >= 4) + chunk_size = strtol(argv[3],0,10); + initialize_function_pointers(); @@ -45,12 +55,24 @@ int main(int argc, char** argv) assert(ifptr.is_open()); } + vector tc_vector; + tc_vector.clear(); testcase tc; while(1) { int break_value = use_old_read_testcase ? read_testcase(&tc, fptr) : read_mod_testcase(ifptr,&tc,true); if(break_value < 0) break; + tc_vector.push_back(tc); + } + vector results_vec; + results_vec.clear(); + results_vec.resize(tc_vector.size()); + double start_time = getCurrClk(); +#pragma omp parallel for schedule(dynamic,chunk_size) num_threads(12) + for(unsigned i=0;i(&tc); baseline_result = log10(baseline_result) - log10(ldexp(1.0, 1020.0)); - cout << std::scientific << baseline_result << " "< 1e-5 && rel_error > 1e-5) + cout << std::scientific << baseline_result << " "< NUMBER GEN_INTRINSIC(GEN_INTRINSIC(compute_full_prob_,SIM GEN_INTRINSIC(GEN_INTRINSIC(init_masks_for_row_,SIMD_TYPE), PRECISION)(*tc, rsArr, lastMaskShiftOut, i*AVX_LENGTH+1, AVX_LENGTH) ; #endif // Since there are no shift intrinsics in AVX, keep the masks in 2 SSE vectors - MASK_VEC currMaskVecLow ; // corresponding to lower half - MASK_VEC currMaskVecHigh ; // corresponding to upper half + + BITMASK_VEC bitMaskVec ; for (int d=1;d NUMBER GEN_INTRINSIC(GEN_INTRINSIC(compute_full_prob_,SIM sumX = VEC_SET1_VAL(zero); // Since there are no shift intrinsics in AVX, keep the masks in 2 SSE vectors - MASK_VEC currMaskVecLow ; // corresponding to lower half - MASK_VEC currMaskVecHigh ; // corresponding to upper half + BITMASK_VEC bitMaskVec ; for (int d=1;d; diff --git a/PairHMM_JNI/vector_defs.h b/PairHMM_JNI/vector_defs.h index c89f7f932..6574f4a76 100644 --- a/PairHMM_JNI/vector_defs.h +++ b/PairHMM_JNI/vector_defs.h @@ -17,6 +17,7 @@ #define SIMD_TYPE sse #define SIMD_TYPE_SSE + #include "define-sse-float.h" #include "vector_function_prototypes.h" From 88c08e78e77ecc52eb1af08c670de7b81ed06b5b Mon Sep 17 00:00:00 2001 From: Karthik Gururaj Date: Tue, 21 Jan 2014 09:57:14 -0800 Subject: [PATCH 06/10] 1. Inserted #define in sandbox pairhmm-template-main.cc 2. Wrapped _mm_empty() with ifdef SIMD_TYPE_SSE 3. OpenMP disabled 4. Added code for initializing PairHMM's data inside initializePairHMM - not used yet --- PairHMM_JNI/libJNILoglessPairHMM.so | Bin 102085 -> 102053 bytes PairHMM_JNI/pairhmm-1-base.cc | 76 ++++++++++-------- PairHMM_JNI/pairhmm-template-kernel.cc | 4 +- PairHMM_JNI/pairhmm-template-main.cc | 12 ++- PairHMM_JNI/utils.cc | 1 + PairHMM_JNI/vector_defs.h | 3 + PairHMM_JNI/vector_function_prototypes.h | 20 ++--- .../PairHMMLikelihoodCalculationEngine.java | 2 +- .../utils/pairhmm/JNILoglessPairHMM.java | 47 +++++++++++ .../sting/utils/pairhmm/PairHMM.java | 12 +++ 10 files changed, 126 insertions(+), 51 deletions(-) diff --git a/PairHMM_JNI/libJNILoglessPairHMM.so b/PairHMM_JNI/libJNILoglessPairHMM.so index 053b901cd7d16182b21054593096ab2ad1c2a0b2..e18552b6b9147e44fc72618991b21b560945e537 100755 GIT binary patch delta 12981 zcmZvD4O~>!*8bTeDAY!Ahs3?AAn)p>}dO^io2qd

|cY1a&#|FiZP>gfBP-%r+go@ej1_gQQ2 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 diff --git a/PairHMM_JNI/pairhmm-1-base.cc b/PairHMM_JNI/pairhmm-1-base.cc index 3941c92de..a2d5dfa88 100644 --- a/PairHMM_JNI/pairhmm-1-base.cc +++ b/PairHMM_JNI/pairhmm-1-base.cc @@ -58,47 +58,53 @@ int main(int argc, char** argv) vector tc_vector; tc_vector.clear(); testcase tc; + double total_time = 0; while(1) { int break_value = use_old_read_testcase ? read_testcase(&tc, fptr) : read_mod_testcase(ifptr,&tc,true); + if(break_value >= 0) + tc_vector.push_back(tc); + if(tc_vector.size() == BATCH_SIZE || (break_value < 0 && tc_vector.size() > 0)) + { + vector results_vec; + results_vec.clear(); + results_vec.resize(tc_vector.size()); + double start_time = getCurrClk(); +#pragma omp parallel for schedule(dynamic,chunk_size) num_threads(12) + for(unsigned i=0;i(&tc); + baseline_result = log10(baseline_result) - log10(ldexp(1.0, 1020.0)); + double abs_error = fabs(baseline_result-results_vec[i]); + double rel_error = (baseline_result != 0) ? fabs(abs_error/baseline_result) : 0; + if(abs_error > 1e-5 && rel_error > 1e-5) + cout << std::scientific << baseline_result << " "< results_vec; - results_vec.clear(); - results_vec.resize(tc_vector.size()); - double start_time = getCurrClk(); -#pragma omp parallel for schedule(dynamic,chunk_size) num_threads(12) - for(unsigned i=0;i(&tc); - baseline_result = log10(baseline_result) - log10(ldexp(1.0, 1020.0)); - double abs_error = fabs(baseline_result-results_vec[i]); - double rel_error = (baseline_result != 0) ? fabs(abs_error/baseline_result) : 0; - if(abs_error > 1e-5 && rel_error > 1e-5) - cout << std::scientific << baseline_result << " "< void GEN_INTRINSIC(GEN_INTRINSIC(initializeVectors,SIMD_TYPE), PRECISION)(int ROWS, int COLS, NUMBER* shiftOutM, NUMBER *shiftOutX, NUMBER *shiftOutY, Context ctx, testcase *tc, _256_TYPE *p_MM, _256_TYPE *p_GAPM, _256_TYPE *p_MX, _256_TYPE *p_XX, _256_TYPE *p_MY, _256_TYPE *p_YY, _256_TYPE *distm1D); -template void GEN_INTRINSIC(GEN_INTRINSIC(stripINITIALIZATION,SIMD_TYPE), PRECISION)( +inline void GEN_INTRINSIC(GEN_INTRINSIC(_vector_shift,SIMD_TYPE), PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn, MAIN_TYPE &shiftOut); +inline void GEN_INTRINSIC(GEN_INTRINSIC(_vector_shift_last,SIMD_TYPE), PRECISION) (UNION_TYPE &x, MAIN_TYPE shiftIn); +inline void GEN_INTRINSIC(GEN_INTRINSIC(precompute_masks_,SIMD_TYPE), PRECISION)(const testcase& tc, int COLS, int numMaskVecs, MASK_TYPE (*maskArr)[NUM_DISTINCT_CHARS]); +inline void GEN_INTRINSIC(GEN_INTRINSIC(init_masks_for_row_,SIMD_TYPE), PRECISION)(const testcase& tc, char* rsArr, MASK_TYPE* lastMaskShiftOut, int beginRowIndex, int numRowsToProcess); +inline void GEN_INTRINSIC(GEN_INTRINSIC(update_masks_for_cols_,SIMD_TYPE), PRECISION)(int maskIndex, MASK_VEC& currMaskVecLow, MASK_VEC& currMaskVecHigh, MASK_TYPE (*maskArr) [NUM_DISTINCT_CHARS], char* rsArr, MASK_TYPE* lastMaskShiftOut, MASK_TYPE maskBitCnt); +inline void GEN_INTRINSIC(GEN_INTRINSIC(computeDistVec,SIMD_TYPE), PRECISION) (MASK_VEC& currMaskVecLow, MASK_VEC& currMaskVecHigh, _256_TYPE& distm, _256_TYPE& _1_distm, _256_TYPE& distmChosen); +template inline void GEN_INTRINSIC(GEN_INTRINSIC(initializeVectors,SIMD_TYPE), PRECISION)(int ROWS, int COLS, NUMBER* shiftOutM, NUMBER *shiftOutX, NUMBER *shiftOutY, Context ctx, testcase *tc, _256_TYPE *p_MM, _256_TYPE *p_GAPM, _256_TYPE *p_MX, _256_TYPE *p_XX, _256_TYPE *p_MY, _256_TYPE *p_YY, _256_TYPE *distm1D); +template inline void GEN_INTRINSIC(GEN_INTRINSIC(stripINITIALIZATION,SIMD_TYPE), PRECISION)( int stripIdx, Context ctx, testcase *tc, _256_TYPE &pGAPM, _256_TYPE &pMM, _256_TYPE &pMX, _256_TYPE &pXX, _256_TYPE &pMY, _256_TYPE &pYY, _256_TYPE &rs, UNION_TYPE &rsN, _256_TYPE &distm, _256_TYPE &_1_distm, _256_TYPE *distm1D, _256_TYPE N_packed256, _256_TYPE *p_MM , _256_TYPE *p_GAPM , _256_TYPE *p_MX, _256_TYPE *p_XX , _256_TYPE *p_MY, _256_TYPE *p_YY, UNION_TYPE &M_t_2, UNION_TYPE &X_t_2, UNION_TYPE &M_t_1, UNION_TYPE &X_t_1, UNION_TYPE &Y_t_2, UNION_TYPE &Y_t_1, UNION_TYPE &M_t_1_y, NUMBER* shiftOutX, NUMBER* shiftOutM); -_256_TYPE GEN_INTRINSIC(GEN_INTRINSIC(computeDISTM,SIMD_TYPE), PRECISION)(int d, int COLS, testcase * tc, HAP_TYPE &hap, _256_TYPE rs, UNION_TYPE rsN, _256_TYPE N_packed256, +inline _256_TYPE GEN_INTRINSIC(GEN_INTRINSIC(computeDISTM,SIMD_TYPE), PRECISION)(int d, int COLS, testcase * tc, HAP_TYPE &hap, _256_TYPE rs, UNION_TYPE rsN, _256_TYPE N_packed256, _256_TYPE distm, _256_TYPE _1_distm); -void GEN_INTRINSIC(GEN_INTRINSIC(computeMXY,SIMD_TYPE), PRECISION)(UNION_TYPE &M_t, UNION_TYPE &X_t, UNION_TYPE &Y_t, UNION_TYPE &M_t_y, +inline void GEN_INTRINSIC(GEN_INTRINSIC(computeMXY,SIMD_TYPE), PRECISION)(UNION_TYPE &M_t, UNION_TYPE &X_t, UNION_TYPE &Y_t, UNION_TYPE &M_t_y, UNION_TYPE M_t_2, UNION_TYPE X_t_2, UNION_TYPE Y_t_2, UNION_TYPE M_t_1, UNION_TYPE X_t_1, UNION_TYPE M_t_1_y, UNION_TYPE Y_t_1, _256_TYPE pMM, _256_TYPE pGAPM, _256_TYPE pMX, _256_TYPE pXX, _256_TYPE pMY, _256_TYPE pYY, _256_TYPE distmSel); template NUMBER GEN_INTRINSIC(GEN_INTRINSIC(compute_full_prob_,SIMD_TYPE), PRECISION) (testcase *tc, NUMBER *before_last_log = NULL); diff --git a/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/PairHMMLikelihoodCalculationEngine.java b/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/PairHMMLikelihoodCalculationEngine.java index 5ab49b531..caa880b41 100644 --- a/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/PairHMMLikelihoodCalculationEngine.java +++ b/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/PairHMMLikelihoodCalculationEngine.java @@ -332,7 +332,7 @@ public class PairHMMLikelihoodCalculationEngine implements LikelihoodCalculation } // initialize arrays to hold the probabilities of being in the match, insertion and deletion cases - pairHMMThreadLocal.get().initialize(X_METRIC_LENGTH, Y_METRIC_LENGTH); + pairHMMThreadLocal.get().initialize(haplotypes, perSampleReadList, X_METRIC_LENGTH, Y_METRIC_LENGTH); } diff --git a/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java b/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java index ca3db5ed3..ed628f064 100644 --- a/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java +++ b/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java @@ -57,6 +57,7 @@ import org.broadinstitute.variant.variantcontext.Allele; import java.util.List; import java.util.Map; +import java.util.HashMap; /** @@ -147,6 +148,52 @@ public class JNILoglessPairHMM extends LoglessPairHMM { jniInitialize(readMaxLength, haplotypeMaxLength); } + private native void jniInitialize(final int numHaplotypes, final int numTotalReads, JNIHaplotypeDataHolderClass[] haplotypeDataArray, + JNIReadDataHolderClass[] readDataArray); + HashMap sampleToIndex = new HashMap(); + //Used to transfer data to JNI + /** + * {@inheritDoc} + */ + @Override + public void initialize( final List haplotypes, final Map> perSampleReadList, final int readMaxLength, final int haplotypeMaxLength ) { + if(verify) + super.initialize(haplotypes, perSampleReadList, readMaxLength, haplotypeMaxLength); + /* + int numHaplotypes = haplotypes.size(); + JNIHaplotypeDataHolderClass[] haplotypeDataArray = new JNIHaplotypeDataHolderClass[numHaplotypes]; + int idx = 0; + for(final Haplotype currHaplotype : haplotypes) + { + haplotypeDataArray[idx] = new JNIHaplotypeDataHolderClass(); + haplotypeDataArray[idx].haplotypeBases = currHaplotype.getBases(); + ++idx; + } + sampleToIndex.clear(); + int totalNumReads = 0; + for(final Map.Entry> currEntry : perSampleReadList) + { + sampleToIndex.put(currEntry.getKey(), totalNumReads); + totalNumReads += currEntry.getValue().size(); + } + + JNIHaplotypeDataHolderClass[] readDataArray = new JNIReadDataHolderClass[totalNumReads]; + idx = 0; + for(final Map.Entry> currEntry : perSampleReadList) + { + for(GATKSAMRecord read : currEntry.getValue()) + { + readDataArray[idx] = new JNIReadDataHolderClass(); + readDataArray[idx].readBases = read.getReadBases(); + readDataArray[idx].readQuals = read.getBaseQualities(); + readDataArray[idx].insertionGOP = read.getBaseInsertionQualities(); + readDataArray[idx].deletionGOP = read.getBaseDeletionQualities(); + readDataArray[idx].overallGCP = GCPArrayMap.get(read); + ++idx; + } + } + jniInitialize(numHaplotypes, numTotalReads, haplotypeDataArray, readDataArray);*/ + } //Real compute kernel private native void jniComputeLikelihoods(int numReads, int numHaplotypes, diff --git a/public/java/src/org/broadinstitute/sting/utils/pairhmm/PairHMM.java b/public/java/src/org/broadinstitute/sting/utils/pairhmm/PairHMM.java index 7b09fe047..08a49cfb7 100644 --- a/public/java/src/org/broadinstitute/sting/utils/pairhmm/PairHMM.java +++ b/public/java/src/org/broadinstitute/sting/utils/pairhmm/PairHMM.java @@ -100,6 +100,18 @@ public abstract class PairHMM { initialized = true; } + /** + * Initialize this PairHMM, making it suitable to run against a read and haplotype with given lengths + * This function is used by the JNI implementations to transfer all data once to the native code + * @param haplotypes the list of haplotypes + * @param perSampleReadList map from sample name to list of reads + * @param haplotypeMaxLength the max length of haplotypes we want to use with this PairHMM + * @param readMaxLength the max length of reads we want to use with this PairHMM + */ + public void initialize( final List haplotypes, final Map> perSampleReadList, final int readMaxLength, final int haplotypeMaxLength ) { + initialize(readMaxLength, haplotypeMaxLength); + } + protected int findMaxReadLength(final List reads) { int listMaxReadLength = 0; for(GATKSAMRecord read : reads){ From 1b1c0c8e7618b7b8ae4c37745aea8f3e1601fdaf Mon Sep 17 00:00:00 2001 From: mozdal Date: Tue, 21 Jan 2014 11:47:30 -0800 Subject: [PATCH 07/10] Split the inner loop to avoid the overhead incurred when -fPIC flag is enabled. --- PairHMM_JNI/pairhmm-template-kernel.cc | 80 +++++++++++++++----------- PairHMM_JNI/utils.cc | 2 +- 2 files changed, 47 insertions(+), 35 deletions(-) diff --git a/PairHMM_JNI/pairhmm-template-kernel.cc b/PairHMM_JNI/pairhmm-template-kernel.cc index 44d205869..66dc557aa 100644 --- a/PairHMM_JNI/pairhmm-template-kernel.cc +++ b/PairHMM_JNI/pairhmm-template-kernel.cc @@ -345,33 +345,38 @@ template NUMBER GEN_INTRINSIC(GEN_INTRINSIC(compute_full_prob_,SIM BITMASK_VEC bitMaskVec ; - for (int d=1;d NUMBER GEN_INTRINSIC(GEN_INTRINSIC(compute_full_prob_,SIM // Since there are no shift intrinsics in AVX, keep the masks in 2 SSE vectors BITMASK_VEC bitMaskVec ; - for (int d=1;d; From 868a8394f7fc9b67277a8095e085a6610ca9f401 Mon Sep 17 00:00:00 2001 From: Karthik Gururaj Date: Tue, 21 Jan 2014 15:01:09 -0800 Subject: [PATCH 08/10] Deleted libJNILoglessPairHMM.so from git tracking --- PairHMM_JNI/.gitignore | 2 +- PairHMM_JNI/libJNILoglessPairHMM.so | Bin 102331 -> 0 bytes 2 files changed, 1 insertion(+), 1 deletion(-) delete mode 100755 PairHMM_JNI/libJNILoglessPairHMM.so diff --git a/PairHMM_JNI/.gitignore b/PairHMM_JNI/.gitignore index a68330124..ce903a6fa 100644 --- a/PairHMM_JNI/.gitignore +++ b/PairHMM_JNI/.gitignore @@ -1,6 +1,6 @@ .svn *.o -#*.so +*.so tests .deps hmm_Mohammad diff --git a/PairHMM_JNI/libJNILoglessPairHMM.so b/PairHMM_JNI/libJNILoglessPairHMM.so deleted file mode 100755 index eed82186343d5ec4ae68941ccd7c95e5e6b55cbe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 102331 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 Date: Wed, 22 Jan 2014 10:52:41 -0800 Subject: [PATCH 09/10] Added support to transfer haplotypes once per region to the JNI Re-use transferred haplotypes (stored in GlobalRef) across calls to computeLikelihoods --- PairHMM_JNI/LoadTimeInitializer.cc | 76 ++++++ PairHMM_JNI/LoadTimeInitializer.h | 37 +++ PairHMM_JNI/Makefile | 13 +- ...e_sting_utils_pairhmm_JNILoglessPairHMM.cc | 228 ++++++++---------- ...te_sting_utils_pairhmm_JNILoglessPairHMM.h | 57 +++-- PairHMM_JNI/pairhmm-1-base.cc | 20 +- PairHMM_JNI/pairhmm-template-main.cc | 7 +- PairHMM_JNI/run.sh | 4 +- PairHMM_JNI/utils.cc | 5 + PairHMM_JNI/utils.h | 2 + .../PairHMMLikelihoodCalculationEngine.java | 8 + .../utils/pairhmm/JNILoglessPairHMM.java | 130 +++++----- .../sting/utils/pairhmm/PairHMM.java | 8 + 13 files changed, 359 insertions(+), 236 deletions(-) create mode 100644 PairHMM_JNI/LoadTimeInitializer.cc create mode 100644 PairHMM_JNI/LoadTimeInitializer.h diff --git a/PairHMM_JNI/LoadTimeInitializer.cc b/PairHMM_JNI/LoadTimeInitializer.cc new file mode 100644 index 000000000..946451952 --- /dev/null +++ b/PairHMM_JNI/LoadTimeInitializer.cc @@ -0,0 +1,76 @@ +#include "LoadTimeInitializer.h" +#include "utils.h" +using namespace std; + +LoadTimeInitializer g_load_time_initializer; + +LoadTimeInitializer::LoadTimeInitializer() //will be called when library is loaded +{ + ConvertChar::init(); + m_sumNumReads = 0; + m_sumSquareNumReads = 0; + m_sumNumHaplotypes = 0; + m_sumSquareNumHaplotypes = 0; + m_sumNumTestcases = 0; + m_sumSquareNumTestcases = 0; + m_maxNumTestcases = 0; + m_num_invocations = 0; + m_compute_time = 0; + m_data_transfer_time = 0; + + m_filename_to_fptr.clear(); + + initialize_function_pointers(); + cout.flush(); +} + +void LoadTimeInitializer::print_profiling() +{ + double mean_val; + cout << "Compute time "<::iterator mI = m_filename_to_fptr.find(filename); + ofstream* fptr = 0; + if(mI == m_filename_to_fptr.end()) + { + m_filename_to_fptr[filename] = new ofstream(); + fptr = m_filename_to_fptr[filename]; + fptr->open(filename.c_str(), to_append ? ios::app : ios::out); + assert(fptr->is_open()); + } + else + fptr = (*mI).second; + //ofstream fptr; + //fptr.open(filename.c_str(), to_append ? ofstream::app : ofstream::out); + (*fptr) << s; + if(add_newline) + (*fptr) << "\n"; + //fptr.close(); +} +void LoadTimeInitializer::debug_close() +{ + for(map::iterator mB = m_filename_to_fptr.begin(), mE = m_filename_to_fptr.end(); + mB != mE;mB++) + { + (*mB).second->close(); + delete (*mB).second; + } + m_filename_to_fptr.clear(); +} + diff --git a/PairHMM_JNI/LoadTimeInitializer.h b/PairHMM_JNI/LoadTimeInitializer.h new file mode 100644 index 000000000..8a03ea0f4 --- /dev/null +++ b/PairHMM_JNI/LoadTimeInitializer.h @@ -0,0 +1,37 @@ +#ifndef LOAD_TIME_INITIALIZER_H +#define LOAD_TIME_INITIALIZER_H +#include "headers.h" +#include +class LoadTimeInitializer +{ + public: + LoadTimeInitializer(); //will be called when library is loaded + void print_profiling(); + void debug_dump(std::string filename, std::string s, bool to_append, bool add_newline=true); + void debug_close(); + + jfieldID m_readBasesFID; + jfieldID m_readQualsFID; + jfieldID m_insertionGOPFID; + jfieldID m_deletionGOPFID; + jfieldID m_overallGCPFID; + jfieldID m_haplotypeBasesFID; + //used to compute avg, variance of #testcases + double m_sumNumReads; + double m_sumSquareNumReads; + double m_sumNumHaplotypes; + double m_sumSquareNumHaplotypes; + double m_sumNumTestcases; + double m_sumSquareNumTestcases; + unsigned m_maxNumTestcases; + unsigned m_num_invocations; + //timing + double m_compute_time; + double m_data_transfer_time; + private: + std::map m_filename_to_fptr; +}; +extern LoadTimeInitializer g_load_time_initializer; + + +#endif diff --git a/PairHMM_JNI/Makefile b/PairHMM_JNI/Makefile index f6dfbb1f0..7506279ef 100644 --- a/PairHMM_JNI/Makefile +++ b/PairHMM_JNI/Makefile @@ -1,4 +1,5 @@ -#OMPCFLAGS=-fopenmp +OMPCFLAGS=-fopenmp +OMPLFLAGS=-fopenmp #-openmp-link static #CFLAGS=-O2 -std=c++11 -W -Wall -march=corei7-avx -Wa,-q -pedantic $(OMPCFLAGS) -Wno-unknown-pragmas #CFLAGS=-O2 -W -Wall -march=corei7 -mfpmath=sse -msse4.2 -pedantic $(OMPCFLAGS) -Wno-unknown-pragmas @@ -19,7 +20,7 @@ DEPDIR=.deps DF=$(DEPDIR)/$(*).d #Common across libJNI and sandbox -COMMON_SOURCES=utils.cc avx_function_instantiations.cc baseline.cc sse_function_instantiations.cc +COMMON_SOURCES=utils.cc avx_function_instantiations.cc baseline.cc sse_function_instantiations.cc LoadTimeInitializer.cc #Part of libJNI LIBSOURCES=org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc $(COMMON_SOURCES) SOURCES=$(LIBSOURCES) pairhmm-template-main.cc pairhmm-1-base.cc @@ -28,7 +29,7 @@ COMMON_OBJECTS=$(COMMON_SOURCES:.cc=.o) #No vectorization for these files -NO_VECTOR_SOURCES=org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc pairhmm-template-main.cc pairhmm-1-base.cc utils.cc baseline.cc +NO_VECTOR_SOURCES=org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc pairhmm-template-main.cc pairhmm-1-base.cc utils.cc baseline.cc LoadTimeInitializer.cc #Use -xAVX for these files AVX_SOURCES=avx_function_instantiations.cc #Use -xSSE4.2 for these files @@ -47,13 +48,13 @@ all: $(BIN) -include $(addprefix $(DEPDIR)/,$(SOURCES:.cc=.d)) checker: pairhmm-1-base.o $(COMMON_OBJECTS) - $(CXX) $(OMPCFLAGS) -o $@ $^ $(LDFLAGS) + $(CXX) $(OMPLFLAGS) -o $@ $^ $(LDFLAGS) pairhmm-template-main: pairhmm-template-main.o $(COMMON_OBJECTS) - $(CXX) $(OMPCFLAGS) -o $@ $^ $(LDFLAGS) + $(CXX) $(OMPLFLAGS) -o $@ $^ $(LDFLAGS) libJNILoglessPairHMM.so: $(LIBOBJECTS) - $(CXX) $(OMPCFLAGS) -shared -o $@ $(LIBOBJECTS) + $(CXX) $(OMPLFLAGS) -shared -o $@ $(LIBOBJECTS) $(OBJECTS): %.o: %.cc diff --git a/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc b/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc index a746bc265..741957fc0 100644 --- a/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc +++ b/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc @@ -11,96 +11,10 @@ #include "template.h" #include "utils.h" +#include "LoadTimeInitializer.h" using namespace std; -class LoadTimeInitializer -{ - public: - LoadTimeInitializer() //will be called when library is loaded - { - ConvertChar::init(); - m_sumNumReads = 0; - m_sumSquareNumReads = 0; - m_sumNumHaplotypes = 0; - m_sumSquareNumHaplotypes = 0; - m_sumNumTestcases = 0; - m_sumSquareNumTestcases = 0; - m_maxNumTestcases = 0; - m_num_invocations = 0; - m_filename_to_fptr.clear(); - - initialize_function_pointers(); - cout.flush(); - } - void print_profiling() - { - double mean_val; - cout <<"Invocations : "<::iterator mI = m_filename_to_fptr.find(filename); - ofstream* fptr = 0; - if(mI == m_filename_to_fptr.end()) - { - m_filename_to_fptr[filename] = new ofstream(); - fptr = m_filename_to_fptr[filename]; - fptr->open(filename.c_str(), to_append ? ios::app : ios::out); - assert(fptr->is_open()); - } - else - fptr = (*mI).second; - //ofstream fptr; - //fptr.open(filename.c_str(), to_append ? ofstream::app : ofstream::out); - (*fptr) << s; - if(add_newline) - (*fptr) << "\n"; - //fptr.close(); - } - void debug_close() - { - for(map::iterator mB = m_filename_to_fptr.begin(), mE = m_filename_to_fptr.end(); - mB != mE;mB++) - { - (*mB).second->close(); - delete (*mB).second; - } - m_filename_to_fptr.clear(); - } - jfieldID m_readBasesFID; - jfieldID m_readQualsFID; - jfieldID m_insertionGOPFID; - jfieldID m_deletionGOPFID; - jfieldID m_overallGCPFID; - jfieldID m_haplotypeBasesFID; - //used to compute avg, variance of #testcases - double m_sumNumReads; - double m_sumSquareNumReads; - double m_sumNumHaplotypes; - double m_sumSquareNumHaplotypes; - double m_sumNumTestcases; - double m_sumSquareNumTestcases; - unsigned m_maxNumTestcases; - unsigned m_num_invocations; - private: - map m_filename_to_fptr; -}; -LoadTimeInitializer g_load_time_initializer; - - JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniInitializeProbabilities (JNIEnv* env, jclass thisObject, jobjectArray transition, jbyteArray insertionGOP, jbyteArray deletionGOP, jbyteArray overallGCP @@ -128,7 +42,7 @@ Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniInitializePrior //Gets direct access to Java arrays #define GET_BYTE_ARRAY_ELEMENTS env->GetPrimitiveArrayCritical #define RELEASE_BYTE_ARRAY_ELEMENTS env->ReleasePrimitiveArrayCritical -#define JNI_RELEASE_MODE 0 +#define JNI_RO_RELEASE_MODE JNI_ABORT #define GET_DOUBLE_ARRAY_ELEMENTS env->GetPrimitiveArrayCritical #define RELEASE_DOUBLE_ARRAY_ELEMENTS env->ReleasePrimitiveArrayCritical @@ -136,7 +50,7 @@ Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniInitializePrior //Likely makes copy of Java arrays to JNI C++ space #define GET_BYTE_ARRAY_ELEMENTS env->GetByteArrayElements #define RELEASE_BYTE_ARRAY_ELEMENTS env->ReleaseByteArrayElements -#define JNI_RELEASE_MODE JNI_ABORT +#define JNI_RO_RELEASE_MODE JNI_ABORT #define GET_DOUBLE_ARRAY_ELEMENTS env->GetDoubleArrayElements #define RELEASE_DOUBLE_ARRAY_ELEMENTS env->ReleaseDoubleArrayElements @@ -187,12 +101,12 @@ Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniSubComputeReadL #endif - RELEASE_BYTE_ARRAY_ELEMENTS(overallGCP, overallGCPArray, JNI_RELEASE_MODE); - RELEASE_BYTE_ARRAY_ELEMENTS(deletionGOP, deletionGOPArray, JNI_RELEASE_MODE); - RELEASE_BYTE_ARRAY_ELEMENTS(insertionGOP, insertionGOPArray, JNI_RELEASE_MODE); - RELEASE_BYTE_ARRAY_ELEMENTS(readQuals, readQualsArray, JNI_RELEASE_MODE); - RELEASE_BYTE_ARRAY_ELEMENTS(haplotypeBases, haplotypeBasesArray, JNI_RELEASE_MODE); - RELEASE_BYTE_ARRAY_ELEMENTS(readBases, readBasesArray, JNI_RELEASE_MODE); + RELEASE_BYTE_ARRAY_ELEMENTS(overallGCP, overallGCPArray, JNI_RO_RELEASE_MODE); + RELEASE_BYTE_ARRAY_ELEMENTS(deletionGOP, deletionGOPArray, JNI_RO_RELEASE_MODE); + RELEASE_BYTE_ARRAY_ELEMENTS(insertionGOP, insertionGOPArray, JNI_RO_RELEASE_MODE); + RELEASE_BYTE_ARRAY_ELEMENTS(readQuals, readQualsArray, JNI_RO_RELEASE_MODE); + RELEASE_BYTE_ARRAY_ELEMENTS(haplotypeBases, haplotypeBasesArray, JNI_RO_RELEASE_MODE); + RELEASE_BYTE_ARRAY_ELEMENTS(readBases, readBasesArray, JNI_RO_RELEASE_MODE); return 0.0; } @@ -226,6 +140,46 @@ JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPai g_load_time_initializer.m_haplotypeBasesFID = fid; } +//Since the list of haplotypes against which the reads are evaluated in PairHMM is the same for a region, +//transfer the list only once +vector > g_haplotypeBasesArrayVector; +JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniInitializeHaplotypes + (JNIEnv * env, jobject thisObject, jint numHaplotypes, jobjectArray haplotypeDataArray) +{ + jboolean is_copy = JNI_FALSE; + //To ensure, GET_BYTE_ARRAY_ELEMENTS is invoked only once for each haplotype, store bytearrays in a vector + vector >& haplotypeBasesArrayVector = g_haplotypeBasesArrayVector; + haplotypeBasesArrayVector.clear(); + haplotypeBasesArrayVector.resize(numHaplotypes); + for(unsigned j=0;jGetObjectArrayElement(haplotypeDataArray, j); + jbyteArray haplotypeBases = (jbyteArray)env->GetObjectField(haplotypeObject, g_load_time_initializer.m_haplotypeBasesFID); +#ifdef ENABLE_ASSERTIONS + assert(haplotypeBases && ("haplotypeBases is NULL at index : "+to_string(j)+"\n").c_str()); +#endif + //Need a global reference as this will be accessed across multiple JNI calls to JNIComputeLikelihoods() + jbyteArray haplotypeBasesGlobalRef = (jbyteArray)env->NewGlobalRef(haplotypeBases); +#ifdef ENABLE_ASSERTIONS + assert(haplotypeBasesGlobalRef && ("Could not get global ref to haplotypeBases at index : "+to_string(j)+"\n").c_str()); +#endif + env->DeleteLocalRef(haplotypeBases); //free the local reference + jbyte* haplotypeBasesArray = (jbyte*)GET_BYTE_ARRAY_ELEMENTS(haplotypeBasesGlobalRef, &is_copy); +#ifdef ENABLE_ASSERTIONS + assert(haplotypeBasesArray && "haplotypeBasesArray not initialized in JNI"); + assert(env->GetArrayLength(haplotypeBasesGlobalRef) < MCOLS); +#endif +#ifdef DEBUG0_1 + cout << "JNI haplotype length "<GetArrayLength(haplotypeBasesGlobalRef)<<"\n"; +#endif + haplotypeBasesArrayVector[j] = make_pair(haplotypeBasesGlobalRef, haplotypeBasesArray); +#ifdef DEBUG3 + for(unsigned k=0;kGetArrayLength(haplotypeBases);++k) + g_load_time_initializer.debug_dump("haplotype_bases_jni.txt",to_string((int)haplotypeBasesArray[k]),true); +#endif + } +} + //JNI function to invoke compute_full_prob_avx //readDataArray - array of JNIReadDataHolderClass objects which contain the readBases, readQuals etc //haplotypeDataArray - array of JNIHaplotypeDataHolderClass objects which contain the haplotypeBases @@ -235,37 +189,16 @@ JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPai (JNIEnv* env, jobject thisObject, jint numReads, jint numHaplotypes, jobjectArray readDataArray, jobjectArray haplotypeDataArray, jdoubleArray likelihoodArray, jint maxNumThreadsToUse) { - jboolean is_copy = JNI_FALSE; - //To ensure, GET_BYTE_ARRAY_ELEMENTS is invoked only once for each haplotype, store bytearrays in a vector - vector > haplotypeBasesArrayVector; - haplotypeBasesArrayVector.clear(); - haplotypeBasesArrayVector.resize(numHaplotypes); #ifdef DEBUG0_1 cout << "JNI numReads "<GetObjectArrayElement(haplotypeDataArray, j); - jbyteArray haplotypeBases = (jbyteArray)env->GetObjectField(haplotypeObject, g_load_time_initializer.m_haplotypeBasesFID); -#ifdef ENABLE_ASSERTIONS - assert(haplotypeBases && ("haplotypeBases is NULL at index : "+to_string(j)+"\n").c_str()); -#endif - jbyte* haplotypeBasesArray = (jbyte*)GET_BYTE_ARRAY_ELEMENTS(haplotypeBases, &is_copy); -#ifdef ENABLE_ASSERTIONS - assert(haplotypeBasesArray && "haplotypeBasesArray not initialized in JNI"); - assert(env->GetArrayLength(haplotypeBases) < MCOLS); -#endif -#ifdef DEBUG0_1 - cout << "JNI haplotype length "<GetArrayLength(haplotypeBases)<<"\n"; -#endif - haplotypeBasesArrayVector[j] = make_pair(haplotypeBases, haplotypeBasesArray); -#ifdef DEBUG3 - for(unsigned k=0;kGetArrayLength(haplotypeBases);++k) - g_load_time_initializer.debug_dump("haplotype_bases_jni.txt",to_string((int)haplotypeBasesArray[k]),true); -#endif - } + double start_time = 0; + //haplotype vector from earlier store - note the reference to vector, not copying + vector >& haplotypeBasesArrayVector = g_haplotypeBasesArrayVector; + jboolean is_copy = JNI_FALSE; unsigned numTestCases = numReads*numHaplotypes; + //vector to store results vector tc_array; tc_array.clear(); tc_array.resize(numTestCases); @@ -274,6 +207,9 @@ JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPai vector > > readBasesArrayVector; readBasesArrayVector.clear(); readBasesArrayVector.resize(numReads); +#ifdef DO_PROFILING + start_time = getCurrClk(); +#endif for(unsigned i=0;iGetArrayLength(likelihoodArray) == numTestCases); #endif +#ifdef DO_PROFILING + start_time = getCurrClk(); +#endif #pragma omp parallel for schedule (dynamic,10) private(tc_idx) num_threads(maxNumThreadsToUse) for(tc_idx=0;tc_idx=0;--i)//note the order - reverse of GET { for(int j=readBasesArrayVector[i].size()-1;j>=0;--j) - RELEASE_BYTE_ARRAY_ELEMENTS(readBasesArrayVector[i][j].first, readBasesArrayVector[i][j].second, JNI_RELEASE_MODE); + RELEASE_BYTE_ARRAY_ELEMENTS(readBasesArrayVector[i][j].first, readBasesArrayVector[i][j].second, JNI_RO_RELEASE_MODE); readBasesArrayVector[i].clear(); } readBasesArrayVector.clear(); - - //Now release haplotype arrays - for(int j=numHaplotypes-1;j>=0;--j) //note the order - reverse of GET - RELEASE_BYTE_ARRAY_ELEMENTS(haplotypeBasesArrayVector[j].first, haplotypeBasesArrayVector[j].second, JNI_RELEASE_MODE); - haplotypeBasesArrayVector.clear(); +#ifdef DO_PROFILING + g_load_time_initializer.m_data_transfer_time += (getCurrClk()-start_time); +#endif tc_array.clear(); #ifdef DO_PROFILING g_load_time_initializer.m_sumNumReads += numReads; @@ -411,6 +356,21 @@ JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPai #endif } +//Release haplotypes at the end of a region +JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniFinalizeRegion + (JNIEnv * env, jobject thisObject) +{ + vector >& haplotypeBasesArrayVector = g_haplotypeBasesArrayVector; + //Now release haplotype arrays + for(int j=haplotypeBasesArrayVector.size()-1;j>=0;--j) //note the order - reverse of GET + { + RELEASE_BYTE_ARRAY_ELEMENTS(haplotypeBasesArrayVector[j].first, haplotypeBasesArrayVector[j].second, JNI_RO_RELEASE_MODE); + env->DeleteGlobalRef(haplotypeBasesArrayVector[j].first); //free the global reference + } + haplotypeBasesArrayVector.clear(); +} + + JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniClose (JNIEnv* env, jobject thisObject) { diff --git a/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.h b/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.h index b50f6a5a9..06ee99d7d 100644 --- a/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.h +++ b/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.h @@ -21,6 +21,34 @@ extern "C" { #define org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_matchToDeletion 4L #undef org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_deletionToDeletion #define org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_deletionToDeletion 5L +#undef org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_debug +#define org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_debug 0L +#undef org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_verify +#define org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_verify 0L +#undef org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_debug0_1 +#define org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_debug0_1 0L +#undef org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_debug1 +#define org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_debug1 0L +#undef org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_debug2 +#define org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_debug2 0L +#undef org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_debug3 +#define org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_debug3 0L +/* + * Class: org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM + * Method: jniGlobalInit + * Signature: (Ljava/lang/Class;Ljava/lang/Class;)V + */ +JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniGlobalInit + (JNIEnv *, jobject, jclass, jclass); + +/* + * Class: org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM + * Method: jniClose + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniClose + (JNIEnv *, jobject); + /* * Class: org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM * Method: jniInitialize @@ -45,39 +73,38 @@ JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPai JNIEXPORT jdouble JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniInitializePriorsAndUpdateCells (JNIEnv *, jobject, jboolean, jint, jint, jbyteArray, jbyteArray, jbyteArray, jint); - /* * Class: org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM * Method: jniSubComputeReadLikelihoodGivenHaplotypeLog10 * Signature: (II[B[B[B[B[B[BI)D */ JNIEXPORT jdouble JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniSubComputeReadLikelihoodGivenHaplotypeLog10 - (JNIEnv *, jobject, jint, jint, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jint); + (JNIEnv *, jobject, jint, jint, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jbyteArray, jint); /* * Class: org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM - * Method: jniGlobalInit - * Signature: (Ljava/lang/Class;Ljava/lang/Class;)V + * Method: jniInitializeHaplotypes + * Signature: (I[Lorg/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM/JNIHaplotypeDataHolderClass;)V */ -JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniGlobalInit - (JNIEnv *, jobject, jclass, jclass); +JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniInitializeHaplotypes + (JNIEnv *, jobject, jint, jobjectArray); + +/* + * Class: org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM + * Method: jniFinalizeRegion + * Signature: ()V + */ +JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniFinalizeRegion + (JNIEnv *, jobject); /* * Class: org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM * Method: jniComputeLikelihoods - * Signature: ([Lorg/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM/JNIReadDataHolderClass;[Lorg/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM/JNIHaplotypeDataHolderClass;[D)V + * Signature: (II[Lorg/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM/JNIReadDataHolderClass;[Lorg/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM/JNIHaplotypeDataHolderClass;[DI)V */ JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniComputeLikelihoods (JNIEnv *, jobject, jint, jint, jobjectArray, jobjectArray, jdoubleArray, jint); -/* - * Class: org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM - * Method: jniClose - * Signature: ()V - */ -JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM_jniClose - (JNIEnv *, jobject); - #ifdef __cplusplus } #endif diff --git a/PairHMM_JNI/pairhmm-1-base.cc b/PairHMM_JNI/pairhmm-1-base.cc index a2d5dfa88..1a2dc9a7c 100644 --- a/PairHMM_JNI/pairhmm-1-base.cc +++ b/PairHMM_JNI/pairhmm-1-base.cc @@ -4,26 +4,15 @@ #include "headers.h" #include "template.h" #include "utils.h" +#include "LoadTimeInitializer.h" using namespace std; -class LoadTimeInitializer -{ - public: - LoadTimeInitializer() //will be called when library is loaded - { - ConvertChar::init(); - } -}; -LoadTimeInitializer g_load_time_initializer; + #define BATCH_SIZE 10000 #define RUN_HYBRID -double getCurrClk() { - struct timeval tv ; - gettimeofday(&tv, NULL); - return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; -} + int main(int argc, char** argv) { @@ -39,9 +28,6 @@ int main(int argc, char** argv) if(argc >= 4) chunk_size = strtol(argv[3],0,10); - - initialize_function_pointers(); - std::ifstream ifptr; FILE* fptr = 0; if(use_old_read_testcase) diff --git a/PairHMM_JNI/pairhmm-template-main.cc b/PairHMM_JNI/pairhmm-template-main.cc index 1c2ca585e..0ab53d378 100644 --- a/PairHMM_JNI/pairhmm-template-main.cc +++ b/PairHMM_JNI/pairhmm-template-main.cc @@ -10,14 +10,9 @@ #define BATCH_SIZE 10000 #define RUN_HYBRID +double getCurrClk(); int thread_level_parallelism_enabled = false ; -double getCurrClk() { - struct timeval tv ; - gettimeofday(&tv, NULL); - return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; -} - void print128b_F(__m128 x) { float *p = (float *)(&x); diff --git a/PairHMM_JNI/run.sh b/PairHMM_JNI/run.sh index de69ba5fc..eafb7ff79 100755 --- a/PairHMM_JNI/run.sh +++ b/PairHMM_JNI/run.sh @@ -1,6 +1,6 @@ #!/bin/bash -rm -f *.txt -export GSA_ROOT_DIR=/home/karthikg/broad/gsa-unstable +rm -f *.txt *.log +GSA_ROOT_DIR=/home/karthikg/broad/gsa-unstable #-Djava.library.path is needed if you are using JNI_LOGLESS_CACHING, else not needed java -Djava.library.path=${GSA_ROOT_DIR}/PairHMM_JNI -jar $GSA_ROOT_DIR/dist/GenomeAnalysisTK.jar -T HaplotypeCaller \ -R /opt/Genomics/ohsu/dnapipeline/humanrefgenome/human_g1k_v37.fasta \ diff --git a/PairHMM_JNI/utils.cc b/PairHMM_JNI/utils.cc index 2288377c4..5ed79e29d 100644 --- a/PairHMM_JNI/utils.cc +++ b/PairHMM_JNI/utils.cc @@ -240,3 +240,8 @@ int read_mod_testcase(ifstream& fptr, testcase* tc, bool reformat) return tokens.size(); } +double getCurrClk() { + struct timeval tv ; + gettimeofday(&tv, NULL); + return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; +} diff --git a/PairHMM_JNI/utils.h b/PairHMM_JNI/utils.h index 8da8269b8..3f7c2ff69 100644 --- a/PairHMM_JNI/utils.h +++ b/PairHMM_JNI/utils.h @@ -1,6 +1,7 @@ #ifndef PAIRHMM_UTIL_H #define PAIRHMM_UTIL_H +#include "template.h" template std::string to_string(T obj) @@ -25,4 +26,5 @@ void debug_dump(std::string filename, std::string s, bool to_append, bool add_ne template NUMBER compute_full_prob(testcase *tc, NUMBER *before_last_log=0); void initialize_function_pointers(); +double getCurrClk(); #endif diff --git a/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/PairHMMLikelihoodCalculationEngine.java b/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/PairHMMLikelihoodCalculationEngine.java index caa880b41..b91805a62 100644 --- a/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/PairHMMLikelihoodCalculationEngine.java +++ b/protected/java/src/org/broadinstitute/sting/gatk/walkers/haplotypecaller/PairHMMLikelihoodCalculationEngine.java @@ -335,6 +335,11 @@ public class PairHMMLikelihoodCalculationEngine implements LikelihoodCalculation pairHMMThreadLocal.get().initialize(haplotypes, perSampleReadList, X_METRIC_LENGTH, Y_METRIC_LENGTH); } + private void finalizePairHMM() + { + pairHMMThreadLocal.get().finalizeRegion(); + } + @Override public Map computeReadLikelihoods( final AssemblyResultSet assemblyResultSet, final Map> perSampleReadList ) { @@ -352,6 +357,9 @@ public class PairHMMLikelihoodCalculationEngine implements LikelihoodCalculation map.filterPoorlyModelledReads(EXPECTED_ERROR_RATE_PER_BASE); stratifiedReadMap.put(sampleEntry.getKey(), map); } + + //Used mostly by the JNI implementation(s) to free arrays + finalizePairHMM(); return stratifiedReadMap; } diff --git a/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java b/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java index ed628f064..f926e4bd6 100644 --- a/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java +++ b/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java @@ -68,8 +68,8 @@ import java.util.HashMap; public class JNILoglessPairHMM extends LoglessPairHMM { private static final boolean debug = false; //simulates ifdef - private static final boolean verify = debug || false; //simulates ifdef - private static final boolean debug0_1 = false; //simulates ifdef + private static final boolean verify = debug || true; //simulates ifdef + private static final boolean debug0_1 = true; //simulates ifdef private static final boolean debug1 = false; //simulates ifdef private static final boolean debug2 = false; private static final boolean debug3 = false; @@ -148,10 +148,10 @@ public class JNILoglessPairHMM extends LoglessPairHMM { jniInitialize(readMaxLength, haplotypeMaxLength); } - private native void jniInitialize(final int numHaplotypes, final int numTotalReads, JNIHaplotypeDataHolderClass[] haplotypeDataArray, - JNIReadDataHolderClass[] readDataArray); - HashMap sampleToIndex = new HashMap(); - //Used to transfer data to JNI + private HashMap haplotypeToHaplotypeListIdxMap = new HashMap(); + private native void jniInitializeHaplotypes(final int numHaplotypes, JNIHaplotypeDataHolderClass[] haplotypeDataArray); + //Used to transfer data to JNI + //Since the haplotypes are the same for all calls to computeLikelihoods within a region, transfer the haplotypes only once to the JNI per region /** * {@inheritDoc} */ @@ -159,7 +159,6 @@ public class JNILoglessPairHMM extends LoglessPairHMM { public void initialize( final List haplotypes, final Map> perSampleReadList, final int readMaxLength, final int haplotypeMaxLength ) { if(verify) super.initialize(haplotypes, perSampleReadList, readMaxLength, haplotypeMaxLength); - /* int numHaplotypes = haplotypes.size(); JNIHaplotypeDataHolderClass[] haplotypeDataArray = new JNIHaplotypeDataHolderClass[numHaplotypes]; int idx = 0; @@ -167,32 +166,22 @@ public class JNILoglessPairHMM extends LoglessPairHMM { { haplotypeDataArray[idx] = new JNIHaplotypeDataHolderClass(); haplotypeDataArray[idx].haplotypeBases = currHaplotype.getBases(); + haplotypeToHaplotypeListIdxMap.put(currHaplotype, idx); ++idx; } - sampleToIndex.clear(); - int totalNumReads = 0; - for(final Map.Entry> currEntry : perSampleReadList) - { - sampleToIndex.put(currEntry.getKey(), totalNumReads); - totalNumReads += currEntry.getValue().size(); - } + jniInitializeHaplotypes(numHaplotypes, haplotypeDataArray); + } - JNIHaplotypeDataHolderClass[] readDataArray = new JNIReadDataHolderClass[totalNumReads]; - idx = 0; - for(final Map.Entry> currEntry : perSampleReadList) - { - for(GATKSAMRecord read : currEntry.getValue()) - { - readDataArray[idx] = new JNIReadDataHolderClass(); - readDataArray[idx].readBases = read.getReadBases(); - readDataArray[idx].readQuals = read.getBaseQualities(); - readDataArray[idx].insertionGOP = read.getBaseInsertionQualities(); - readDataArray[idx].deletionGOP = read.getBaseDeletionQualities(); - readDataArray[idx].overallGCP = GCPArrayMap.get(read); - ++idx; - } - } - jniInitialize(numHaplotypes, numTotalReads, haplotypeDataArray, readDataArray);*/ + private native void jniFinalizeRegion(); + //Tell JNI to release arrays - really important if native code is directly accessing Java memory, if not + //accessing Java memory directly, still important to release memory from C++ + /** + * {@inheritDoc} + */ + @Override + public void finalizeRegion() + { + jniFinalizeRegion(); } //Real compute kernel @@ -216,10 +205,10 @@ public class JNILoglessPairHMM extends LoglessPairHMM { throw new IllegalStateException("Must call initialize before calling jniComputeLikelihoods in debug/verify mode"); } int readListSize = reads.size(); - int alleleHaplotypeMapSize = alleleHaplotypeMap.size(); - int numTestcases = readListSize*alleleHaplotypeMapSize; + int numHaplotypes = alleleHaplotypeMap.size(); + int numTestcases = readListSize*numHaplotypes; if(debug0_1) - System.out.println("Java numReads "+readListSize+" numHaplotypes "+alleleHaplotypeMapSize); + System.out.println("Java numReads "+readListSize+" numHaplotypes "+numHaplotypes); JNIReadDataHolderClass[] readDataArray = new JNIReadDataHolderClass[readListSize]; int idx = 0; for(GATKSAMRecord read : reads) @@ -246,39 +235,68 @@ public class JNILoglessPairHMM extends LoglessPairHMM { } ++idx; } - JNIHaplotypeDataHolderClass[] haplotypeDataArray = new JNIHaplotypeDataHolderClass[alleleHaplotypeMapSize]; - idx = 0; - for (Map.Entry currEntry : alleleHaplotypeMap.entrySet()) //order is important - access in same order always + + JNIHaplotypeDataHolderClass[] haplotypeDataArray = new JNIHaplotypeDataHolderClass[numHaplotypes]; + if(verify) { - haplotypeDataArray[idx] = new JNIHaplotypeDataHolderClass(); - haplotypeDataArray[idx].haplotypeBases = currEntry.getValue().getBases(); - if(debug0_1) - System.out.println("Java haplotype length "+haplotypeDataArray[idx].haplotypeBases.length); - if(debug3) + idx = 0; + for (Map.Entry currEntry : alleleHaplotypeMap.entrySet()) //order is important - access in same order always { - for(int i=0;i currEntry : alleleHaplotypeMap.entrySet())//order is important - access in same order always { - likelihoodMap.add(read, currEntry.getKey(), likelihoodArray[idx]); + //Since the order of haplotypes in the List and alleleHaplotypeMap is different, + //get idx of current haplotype in the list and use this idx to get the right likelihoodValue + idxInsideHaplotypeList = haplotypeToHaplotypeListIdxMap.get(currEntry.getValue()); + likelihoodMap.add(read, currEntry.getKey(), likelihoodArray[readIdx + idxInsideHaplotypeList]); ++idx; } + readIdx += numHaplotypes; + } if(verify) { + //re-order values in likelihoodArray + double[] tmpArray = new double[numHaplotypes]; + idx = 0; + idxInsideHaplotypeList = 0; + readIdx = 0; + for(GATKSAMRecord read : reads) + { + for(int j=0;j currEntry : alleleHaplotypeMap.entrySet())//order is important - access in same order always + { + idxInsideHaplotypeList = haplotypeToHaplotypeListIdxMap.get(currEntry.getValue()); + likelihoodArray[idx] = tmpArray[idxInsideHaplotypeList]; + ++idx; + } + readIdx += numHaplotypes; + } //to compare values likelihoodMap = super.computeLikelihoods(reads, alleleHaplotypeMap, GCPArrayMap); //for floating point values, no exact equality @@ -304,23 +322,23 @@ public class JNILoglessPairHMM extends LoglessPairHMM { if(toDump) { idx = 0; - System.out.println("Dump : Java numReads "+readListSize+" numHaplotypes "+alleleHaplotypeMapSize); + System.out.println("Dump : Java numReads "+readListSize+" numHaplotypes "+numHaplotypes); for(int i=0;i Date: Wed, 22 Jan 2014 22:57:32 -0800 Subject: [PATCH 10/10] 1. Converted q,i,d,c in C++ from int* to char* 2. Use clock_gettime to measure performance 3. Disabled OpenMP 4. Moved LoadTimeInitializer to different file --- PairHMM_JNI/LoadTimeInitializer.cc | 4 +- PairHMM_JNI/LoadTimeInitializer.h | 4 +- PairHMM_JNI/Makefile | 8 ++-- ...e_sting_utils_pairhmm_JNILoglessPairHMM.cc | 40 +++++++++---------- PairHMM_JNI/pairhmm-1-base.cc | 13 ++++-- PairHMM_JNI/run.sh | 6 +-- PairHMM_JNI/template.h | 3 +- PairHMM_JNI/utils.cc | 31 ++++++++++---- PairHMM_JNI/utils.h | 1 + .../utils/pairhmm/JNILoglessPairHMM.java | 4 +- 10 files changed, 66 insertions(+), 48 deletions(-) diff --git a/PairHMM_JNI/LoadTimeInitializer.cc b/PairHMM_JNI/LoadTimeInitializer.cc index 946451952..534043371 100644 --- a/PairHMM_JNI/LoadTimeInitializer.cc +++ b/PairHMM_JNI/LoadTimeInitializer.cc @@ -27,8 +27,8 @@ LoadTimeInitializer::LoadTimeInitializer() //will be called when library is loa void LoadTimeInitializer::print_profiling() { double mean_val; - cout << "Compute time "< m_filename_to_fptr; }; diff --git a/PairHMM_JNI/Makefile b/PairHMM_JNI/Makefile index 7506279ef..9b461a833 100644 --- a/PairHMM_JNI/Makefile +++ b/PairHMM_JNI/Makefile @@ -1,5 +1,5 @@ -OMPCFLAGS=-fopenmp -OMPLFLAGS=-fopenmp #-openmp-link static +#OMPCFLAGS=-fopenmp +#OMPLFLAGS=-fopenmp #-openmp-link static #CFLAGS=-O2 -std=c++11 -W -Wall -march=corei7-avx -Wa,-q -pedantic $(OMPCFLAGS) -Wno-unknown-pragmas #CFLAGS=-O2 -W -Wall -march=corei7 -mfpmath=sse -msse4.2 -pedantic $(OMPCFLAGS) -Wno-unknown-pragmas @@ -11,7 +11,7 @@ COMMON_COMPILATION_FLAGS=$(JNI_COMPILATION_FLAGS) -O3 -W -Wall -pedantic $(OMPCF CC=icc CXX=icc -LDFLAGS=-lm $(OMPLDFLAGS) +LDFLAGS=-lm -lrt $(OMPLDFLAGS) BIN=libJNILoglessPairHMM.so pairhmm-template-main checker #BIN=checker @@ -54,7 +54,7 @@ pairhmm-template-main: pairhmm-template-main.o $(COMMON_OBJECTS) $(CXX) $(OMPLFLAGS) -o $@ $^ $(LDFLAGS) libJNILoglessPairHMM.so: $(LIBOBJECTS) - $(CXX) $(OMPLFLAGS) -shared -o $@ $(LIBOBJECTS) + $(CXX) $(OMPLFLAGS) -shared -o $@ $(LIBOBJECTS) ${LDFLAGS} $(OBJECTS): %.o: %.cc diff --git a/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc b/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc index 741957fc0..d575b8271 100644 --- a/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc +++ b/PairHMM_JNI/org_broadinstitute_sting_utils_pairhmm_JNILoglessPairHMM.cc @@ -208,7 +208,7 @@ JNIEXPORT void JNICALL Java_org_broadinstitute_sting_utils_pairhmm_JNILoglessPai readBasesArrayVector.clear(); readBasesArrayVector.resize(numReads); #ifdef DO_PROFILING - start_time = getCurrClk(); + start_time = get_time(); #endif for(unsigned i=0;iGetArrayLength(likelihoodArray) == numTestCases); #endif #ifdef DO_PROFILING - start_time = getCurrClk(); + start_time = get_time(); #endif #pragma omp parallel for schedule (dynamic,10) private(tc_idx) num_threads(maxNumThreadsToUse) for(tc_idx=0;tc_idx tc_vector; tc_vector.clear(); testcase tc; - double total_time = 0; + uint64_t total_time = 0; while(1) { int break_value = use_old_read_testcase ? read_testcase(&tc, fptr) : read_mod_testcase(ifptr,&tc,true); @@ -55,7 +55,7 @@ int main(int argc, char** argv) vector results_vec; results_vec.clear(); results_vec.resize(tc_vector.size()); - double start_time = getCurrClk(); + get_time(); #pragma omp parallel for schedule(dynamic,chunk_size) num_threads(12) for(unsigned i=0;iihap = (int *) malloc(tc->haplen*sizeof(int)); tc->irs = (int *) malloc(tc->rslen*sizeof(int)); - //tc->q = (int *) malloc(sizeof(int) * tc->rslen); - //tc->i = (int *) malloc(sizeof(int) * tc->rslen); - //tc->d = (int *) malloc(sizeof(int) * tc->rslen); - //tc->c = (int *) malloc(sizeof(int) * tc->rslen); + tc->q = (char *) malloc(sizeof(char) * tc->rslen); + tc->i = (char *) malloc(sizeof(char) * tc->rslen); + tc->d = (char *) malloc(sizeof(char) * tc->rslen); + tc->c = (char *) malloc(sizeof(char) * tc->rslen); for (x = 0; x < tc->rslen; x++) { @@ -199,18 +199,22 @@ int read_mod_testcase(ifstream& fptr, testcase* tc, bool reformat) memcpy(tc->hap, tokens[0].c_str(), tokens[0].size()); tc->rs = new char[tokens[1].size()+2]; tc->rslen = tokens[1].size(); + tc->q = new char[tc->rslen]; + tc->i = new char[tc->rslen]; + tc->d = new char[tc->rslen]; + tc->c = new char[tc->rslen]; //cout << "Lengths "<haplen <<" "<rslen<<"\n"; memcpy(tc->rs, tokens[1].c_str(),tokens[1].size()); assert(tokens.size() == 2 + 4*(tc->rslen)); assert(tc->rslen < MROWS); for(unsigned j=0;jrslen;++j) - tc->q[j] = convToInt(tokens[2+0*tc->rslen+j]); + tc->q[j] = (char)convToInt(tokens[2+0*tc->rslen+j]); for(unsigned j=0;jrslen;++j) - tc->i[j] = convToInt(tokens[2+1*tc->rslen+j]); + tc->i[j] = (char)convToInt(tokens[2+1*tc->rslen+j]); for(unsigned j=0;jrslen;++j) - tc->d[j] = convToInt(tokens[2+2*tc->rslen+j]); + tc->d[j] = (char)convToInt(tokens[2+2*tc->rslen+j]); for(unsigned j=0;jrslen;++j) - tc->c[j] = convToInt(tokens[2+3*tc->rslen+j]); + tc->c[j] = (char)convToInt(tokens[2+3*tc->rslen+j]); if(reformat) { @@ -245,3 +249,14 @@ double getCurrClk() { gettimeofday(&tv, NULL); return (double)tv.tv_sec + (double)tv.tv_usec / 1000000.0; } + +uint64_t get_time(struct timespec* store_struct) +{ + static struct timespec start_time; + struct timespec curr_time; + struct timespec* ptr = (store_struct == 0) ? &curr_time : store_struct; + clock_gettime(CLOCK_REALTIME, ptr); + uint64_t diff_time = (ptr->tv_sec-start_time.tv_sec)*1000000000+(ptr->tv_nsec-start_time.tv_nsec); + start_time = *ptr; + return diff_time; +} diff --git a/PairHMM_JNI/utils.h b/PairHMM_JNI/utils.h index 3f7c2ff69..cc020ba40 100644 --- a/PairHMM_JNI/utils.h +++ b/PairHMM_JNI/utils.h @@ -27,4 +27,5 @@ template NUMBER compute_full_prob(testcase *tc, NUMBER *before_last_log=0); void initialize_function_pointers(); double getCurrClk(); +uint64_t get_time(struct timespec* x=0); #endif diff --git a/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java b/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java index f926e4bd6..6cbd3510c 100644 --- a/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java +++ b/protected/java/src/org/broadinstitute/sting/utils/pairhmm/JNILoglessPairHMM.java @@ -68,8 +68,8 @@ import java.util.HashMap; public class JNILoglessPairHMM extends LoglessPairHMM { private static final boolean debug = false; //simulates ifdef - private static final boolean verify = debug || true; //simulates ifdef - private static final boolean debug0_1 = true; //simulates ifdef + private static final boolean verify = debug || false; //simulates ifdef + private static final boolean debug0_1 = false; //simulates ifdef private static final boolean debug1 = false; //simulates ifdef private static final boolean debug2 = false; private static final boolean debug3 = false;