Openblas: performance on AMD Ryzen and Threadripper

Created on 14 Feb 2018  路  121Comments  路  Source: xianyi/OpenBLAS

This report comes right from #1425 where the discussion drifted off from thread safety in openblas v. 0.2.20 to performance on AMD Ryzen and Threadripper processors (in this particular case a TR 1950X). I seems worthwhile to discuss this in a separate thread. Until now we had the following discussion

@tkswe88 : After plenty of initial tests with the AMD TR 1950X processor, it seems that openblas (tested 0.2.19, 0.2.20 and the development version on Ubuntu 17.10 with kernel 4.13, gcc and gfortran v. 7.2) operates roughly 20% slower on the TR1950X than on an i7-4770 (4 cores) when using 1, 2 and 4 threads. This is somewhat surprising given that both CPUs use at most AVX2 and thus should be comparable in terms of vectorisation potential. I have already adjusted the OpenMP thread affinity to exclude that the (hidden) NUMA architecture of the TR1950X causes its lower performance. Other measures I took were 1) BIOS upgrade, 2) Linux kernel upgrade to 4.15, 3) increased DIMM frequency from 2133 to 2666 MHz. Except for the latter, which gave a speedup of roughly 3%, these measures did not have any effect on execution speed. Do you have any idea where the degraded performance on the TR1950X comes from? Is this related to a current lack of optimization in openblas or do we just have to wait for the next major release of gcc/gfortran to fix the problem? Of course, I would be willing to run tests, if this was of help in developing openblas.

@brada4 : AMD has slightly slower AVX and AVX2 units per CPU, by no means slow in general, it still has heap of cores spare. Sometimes optimal AVX2 saturation means turning whole CPU cartridge to base, i.e non-turpo frequency.

@martin-frbg: Could also be that getarch is mis-detecting the cache sizes on TR, or the various hardcoded block sizes from param.h for loop unrolling are "more wrong" on TR than they were on the smaller Ryzen. Overall support for the Ryzen architecture is currently limited to treating it like Haswell, see #1133,1147. There may be other assembly instructions besides AVX2 that are slower on Ryzen (#1147 mentions movntp*).

@tkswe88: Are there any tests I could do to find out about cache size detection errors or more appropriate settings for loop unroling?

@brada4 What you asked to martin - copied parameters may need doubled or halved at least here: https://github.com/xianyi/OpenBLAS/pull/1133/files#diff-7a3ef0fabb9c6c40aac5ae459c3565f0

@martin-frbg: You could simply check the autodetected information in config.h against the specification. (As far as I can determine, L3 size seems to be ignored as a parameter).
As far as appropriate settings go, the benchmark directory contains a few tests that can be used for timing individual functions. Adjusting the values in param.h (see https://github.com/xianyi/OpenBLAS/pull/1157/files) is a bit of a black art though.

Most helpful comment

@yubeic Your question is far too general to be answered in a satisfactory and fair way. There are many different factors entering for which it is very difficult to guess how they could affect performance on your system. Nevertheless, I try to answer this in a point by point:

1) numpy and Pytorch (Magma): I code in Fortran2008, so I cannot help you here.

2) mkl and openblas: OpenMP or thread parallelism with mkl routines seems fine, but support of the AVX(2) units in the TR1950X by mkl may be a problem (I only have a version from 2017, so no idea whether this was improved in a 2018 version of mkl). In my opinion openblas is the best tuned linear algebra package for the TR1950X at the moment. For some lapack routines, however, the openblas routines do not seem to be OpenMP parallelised or vectorised, which quite heavily impacts performance (one example is Cholesky factorization using DPPSV. I just replaced this by a conversion to general format using DTPTTR and factorization using DPOSV and everything was good). Note that this is not TR1950X specific, but applies to all CPUs. So looking into this a bit for the LAPACK and BLAS routines you use may have a huge impact. @brada4 and @martin-frbg may want to correct me here.

3) i7 6700k: I can imagine that the single-thread to four-thread performance on this system for highly vectorized loads (worst case DGEMM) is about slightly better than that of the i7-4770 that I used for comparison. Nevertheless, if the vector units limit you, you can always just use more threads of the TR1950X to outperform the i7 6700k or just use your GPUs (a sufficiently good GPU should also outperform an i9 7900x in highly vectorial loads). However, by far not all parts of an application are vectorial. So, you better look at the total runtime, which also depends on bandwidth. For my main application, I reach the break even point at about 5 to 5.5 TR1950x cores, when aiming for the same total run-time as using four i7-4770 cores. Of course, this varies also a bit with the problem size.

4) UMA vs. NUMA (not reported in this thread previously, I think): This is not one of your points but can boost performance by 20% per cent in some cases. The standard setting for the TR9150X is UMA. Unfortunately, this cannot be changed to NUMA in the BIOS of at least ASUS mobos and one needs a Windows installation to use the AMD tool. Nevertheless, switching to NUMA, ensuring thread affinity with the appropriate OpenMP settings and running on four threads of one CCX gave a performance boost of 20% in a part of my standard computations that is highly bandwidth dependent (i.e. where I need to access many different arrays and structures to assemble and factorise complex valued matrices) and did not change the performance in other parts of the code. At 8 cores on two CCX, NUMA is still about 10% faster than UMA for this specific part of my code. Anyhow, you would have to test this. It really depends on the code, how well it is parallelised and how rigorously the first-touch principle was or could be followed. One may argue that having a NUMA architecture on a workstation is a drawback, because a bit of care is required in code design. I chose the TR1950X deliberately because of this feature. The bigger machines that I have access to are NUMA. So testing the NUMA performance on my workstation seems to be a good idea to me.

5) i9-7900X: I do not have access to such a system for comparison.

Again, your question is too general to answer.

My TR1950X has been running stably and reliably thus far. So, I am quite happy with it. Sure, when it comes to single-threaded AVX2 workloads, it is slower than some Intel offerings, but I guess it is vectorisable work loads that you must have bought those 4 GPUs for, right?

All 121 comments

Here, I report on a first test with modified cache sizes.

The cache sizes reported from lstopo are
for every core
L1d: 32KB
L1i: 64KB
L2: 512KB
and for each of the four CCXs (4 cores per CCX)
L3: 8MB (i.e. 32 MB in total)

Hence, I performed a test on the current development version.

Assuming L1 and L2 would have to be reported per core and L3 in total, I modified the relevant lines of getarch.c to

if defined (FORCE_ZEN)

define FORCE

define FORCE_INTEL

define ARCHITECTURE "X86"

define SUBARCHITECTURE "ZEN"

#define ARCHCONFIG "-DZEN " \
"-DL1_CODE_SIZE=65536 -DL1_CODE_LINESIZE=64 -DL1_CODE_ASSOCIATIVE=8 " \
"-DL1_DATA_SIZE=32768 -DL1_DATA_LINESIZE=64 -DL2_CODE_ASSOCIATIVE=8 " \
"-DL2_SIZE=524288 -DL2_LINESIZE=64 -DL2_ASSOCIATIVE=8 " \
"-DL3_SIZE=33554432 -DL3_LINESIZE=64 -DL3_ASSOCIATIVE=8 " \
"-DITB_DEFAULT_ENTRIES=64 -DITB_SIZE=4096 " \
"-DDTB_DEFAULT_ENTRIES=64 -DDTB_SIZE=4096 " \
"-DHAVE_MMX -DHAVE_SSE -DHAVE_SSE2 -DHAVE_SSE3 -DHAVE_SSE4_1 -DHAVE_SSE4_2 " \
"-DHAVE_SSE4A -DHAVE_MISALIGNSSE -DHAVE_128BITFPU -DHAVE_FASTMOVU -DHAVE_CFLUSH " \
"-DHAVE_AVX -DHAVE_FMA3 -DFMA3"

define LIBNAME "zen"

define CORENAME "ZEN"

endif

I hope these settings are correctly translated from lstopo.
Next, I ran
sudo make clean
sudo make TARGET=ZEN USE_OPENMP=1 BINARY=64 FC=gfortran

After this, config.h reads as

define OS_LINUX 1

define ARCH_X86_64 1

define C_GCC 1

define __64BIT__ 1

define PTHREAD_CREATE_FUNC pthread_create

define BUNDERSCORE _

define NEEDBUNDERSCORE 1

define ZEN

define L1_CODE_SIZE 65536

define L1_CODE_LINESIZE 64

define L1_CODE_ASSOCIATIVE 8

define L1_DATA_SIZE 32768

define L1_DATA_LINESIZE 64

define L2_CODE_ASSOCIATIVE 8

define L2_SIZE 524288

define L2_LINESIZE 64

define L2_ASSOCIATIVE 8

define L3_SIZE 33554432

define L3_LINESIZE 64

define L3_ASSOCIATIVE 8

define ITB_DEFAULT_ENTRIES 64

define ITB_SIZE 4096

define DTB_DEFAULT_ENTRIES 64

define DTB_SIZE 4096

define HAVE_MMX

define HAVE_SSE

define HAVE_SSE2

define HAVE_SSE3

define HAVE_SSE4_1

define HAVE_SSE4_2

define HAVE_SSE4A

define HAVE_MISALIGNSSE

define HAVE_128BITFPU

define HAVE_FASTMOVU

define HAVE_CFLUSH

define HAVE_AVX

define HAVE_FMA3

define FMA3

define CORE_ZEN

define CHAR_CORENAME "ZEN"

define SLOCAL_BUFFER_SIZE 24576

define DLOCAL_BUFFER_SIZE 32768

define CLOCAL_BUFFER_SIZE 12288

define ZLOCAL_BUFFER_SIZE 8192

define GEMM_MULTITHREAD_THRESHOLD 4

Eventually, I installed using
sudo make PREFIX=/usr/local install

After this, config.h has changed and reads as

define OS_LINUX 1

define ARCH_X86_64 1

define C_GCC 1

define __64BIT__ 1

define PTHREAD_CREATE_FUNC pthread_create

define BUNDERSCORE _

define NEEDBUNDERSCORE 1

define ZEN

define L1_CODE_SIZE 32768

define L1_CODE_ASSOCIATIVE 8

define L1_CODE_LINESIZE 64

define L1_DATA_SIZE 32768

define L1_DATA_ASSOCIATIVE 8

define L1_DATA_LINESIZE 64

define L2_SIZE 524288

define L2_ASSOCIATIVE 8

define L2_LINESIZE 64

define L3_SIZE 33554432

define L3_ASSOCIATIVE 10

define L3_LINESIZE 64

define ITB_SIZE 4096

define ITB_ASSOCIATIVE 0

define ITB_ENTRIES 64

define DTB_SIZE 4096

define DTB_ASSOCIATIVE 0

define DTB_DEFAULT_ENTRIES 64

define HAVE_CMOV

define HAVE_MMX

define HAVE_SSE

define HAVE_SSE2

define HAVE_SSE3

define HAVE_SSSE3

define HAVE_SSE4_1

define HAVE_SSE4_2

define HAVE_SSE4A

define HAVE_AVX

define HAVE_FMA3

define HAVE_CFLUSH

define HAVE_MISALIGNSSE

define HAVE_128BITFPU

define HAVE_FASTMOVU

define NUM_SHAREDCACHE 1

define NUM_CORES 1

define CORE_ZEN

define CHAR_CORENAME "ZEN"

define SLOCAL_BUFFER_SIZE 24576

define DLOCAL_BUFFER_SIZE 32768

define CLOCAL_BUFFER_SIZE 12288

define ZLOCAL_BUFFER_SIZE 8192

define GEMM_MULTITHREAD_THRESHOLD 4

Note that in particular L1_CODE_SIZE was reset to 32KB. Also some of the L?_ASSOCIATIVE values have changed.

Looking at /usr/local/include/openblas_config.h, which was generated during the installation, has copied the entries of config.h generated during compilation (i.e. it has the right L1_CODE_SIZE of 64KB).

I have not observed any performance improvement from the modification of the caches. But I wonder whether the changes to config.h (L1_CODE_SIZE) during installation may have adverse effects on performance.

I will do more tests using HASWELL targets instead of ZEN in the development version and try to find out about loop unrolling.

I suspect as you ran the make install without repeating the previous TARGET=ZEN argument it re-ran getarch, and that seems to misdetect at least the L1 code size. As no part of the library got rebuilt, and the correct version of openblas_config.h got installed this should not cause any problems.
Edited to add: there does not appear to be any use of L1_CODE_SIZE in the code anyway, even L1_DATA_LINESIZE appears to be used by a few old targets only. It would seem that the only hardware parameters to get right would be DTB_ENTRIES (used in level2 BLAS loop unrolling and POTRF) and L2_DATA_SIZE (used for buffer allocation in driver/others/memory.c) . Both seem correct in what you wrote above.

@martin-frbg Your guessed correctly. Including the TRAGET=ZEN argument in the installation let to the right entries in config.h after installation and this did not improve performance.

BTW this was also the case in the original LibGoto - L2 size used only to derive xGEMM_P parameters for Core2, Opteron and earlier, L1 and L3 size apparently unused. Seems most of the hardware parameters are detected and reported "just in case" now, but perhaps cpu development has stabilized in the sense that a given type id will no longer have variants that vary in L1 or L2 properties.
(There is one oddity in l2param.h where it uses L1_DATA_LINESIZE to determine an offset, but again your value appears to be correct already.)

Somewhere deep in Wikipedia it is said that zen-epic-ripper changes cache from write-through to write-back. That may mean that effective cache easily halves.

Somehow I doubt that, or at least its relevance for the detected cache sizes. On the other hand I feel there is a need to find out which of the many parameters in param.h and elsewhere that are faithfully copied for new cpu models are actually used in the current code, and how critical their influence is.
Starting from the fragment of the param.h change I linked to above, it looks to me that SNUMOPT is
completely unused, and DNUMOPT has a single appearance in a debug statement where it seems to
be part of a theoretical efficiency factor for the syrk function.

I have been looking a bit more at the suspected performance shortage on the AMD Threadripper 1950X, its reasons and the consequences of thread-oversubscription on the TR1950X.

  1. Regarding the previously reported 20% lower performance of the TR1950X compared to the i7-4770 using 1, 2 and 4 threads, I need to correct that this was for the total runtime of my code for one particular example. For dposv, dsyrk and zgbtrf using openblas 0.2.19, 0.2.20 and 0.3.30, the i7-4770 needs about 30-35% less time than the TR1950X in my example. I stumbled across a document (seemingly from AMD) on this webpage
    http://32ipi028l5q82yhj72224m8j.wpengine.netdna-cdn.com/wp-content/uploads/2017/03/GDC2017-Optimizing-For-AMD-Ryzen.pdf recommending strongly to avoid software prefetch on Ryzen platforms, because this would prevent loop unrolling. The test example written in C and presented in the pdf reports a 15% speedup by not prefetching. However, the presented example is for compilation using Microsoft Visual Studio, and it is for this setup that software prefetching prevents loop unrolling. Do you think this might be a potential problem in openblas using compilation with gcc or is there hardcoded loop unrolling in openblas which would not be susceptible to this?
    @brada4: On the TR1950X, I monitored the clock speeds on all active cores on /proc/cpuinfo and under load they all seem to run at 3.75 GHz with little variation (base frequency of TR1950X is 3.4 GHz). Under load the frequencies of the i7-4770 were 3.5 to 3.9 GHz (according to i7z_GUI). So this is not a big difference, it seems.

  2. Using 8-16 threads on the TR1950X, I observed a version-related speedup of 2-5% in parallelised dsyrk and dposv for each of these numbers of threads, when switching from v. 0.2.19 to v. 0.2.20 or the development version. For 1 thread, there was no difference. For 4 threads, the improvement was at best 1%. For ZGBTRF and ZGBTRS, I cannot make an educated statement about possible improvements between the versions because of my self-inflicted thread oversubcription (cf. #1425 and below). However, selecting a recent openblas version has advantages unless one oversubscribes on the threads (next point).

  3. Regarding the thread-oversubscription (calling parallelised ZGBTRF and ZGBTRS from within an OpenMP parallelised loop in my code), I run my code linked to openblas 0.2.19, 0.2.20 and the development version. There is an interesting behaviour for the total run-time of this loop with respect to the number of threads and the openblas version:

no. threads runtime 0.2.19 [s] runtime 0.2.20 [s] runtime devel [s]
1 35 32.5 32.5
4 11.7 11.9 11.8
8 8.3 14.5 25.3
12 6.1 20.3 32.3
16 6.1 24.3 40.1

These numbers are for compilation of openblas and my code using gcc/gfortran v. 7.2 with optimisation level -O2 (-O3 reduces the run times by about 1 s). So, up to 4 threads the performance is comparable, and the thread-oversubscription does not really seem to play a big role. For a larger number of threads, v.0.2.19 still sees a decent speedup, but for versions 0.2.20 and devel, there is clear detoriation in performance when opting for a higher number of threads. Note that these examples where run after correcting lapack-netlib/SRC/Makefile of versions 0.2.20 and devel and recompiling (cf. #1425). So, I get correct results in all of these tests.

Since there are general improvements in speed when selecting a recent openblas version, I would want to get over the problem with thread-oversubscription. However, I would need sequential versions of ZGBTRF and ZGBTRS inside the aforementioned loop and parallelised versions of DSYRK, DPOSV, etc in other parts of the code. This seems to require compilation of sequential and parallel versions of the openblas library, linking to these and then somehow picking the right (sequential or parallel) versions of the required BLAS and LAPACK routines. Now, if openblas came as a Fortran module, this would be a no-brainer, because one could just use the "use ..., only :: ... => ..." mechanism of Fortran to restrict import of symbols and to re-name them. I have been searching the net for possible linker-related solutions that provide similar mechanisms, but to no avail. Do you have a document or webpage with a solution to this at hand?

Some thoughts -

  1. prefetch should be addressed automatically by gcc for C and Fortran. The Haswell assembly files have a bunch of prefetcht0 instructions that may need looking at.
  2. Need to look again at what these two functions call internally.
  3. There have been both LAPACK version updates and pthread safety fixes between each pair of releases, and develop has new GEMM thread sheduling from #1320. In addition, develop has a less efficient AXPY at the moment due to #1332 (which would be easy to revert).
    But offhand there is nothing I would immediately blame the observed and rather serious loss of performance on. (This may warrant another git bisect...)

Fine, I will run git bisect on Monday.

Thanks. BTW there is a utility function openblas_set_num_threads() but according to #803 not all parts of the code honor it.

ZGBTRF did not change between LAPACK 3.6.1 and 3.8.0, and is mostly IZAMAX + ZSCAL + ZGERU + ZTRSM + ZGEMM. Nothing I'd immediately identify as having undergone any drastic changes since 0.2.19. (Except the post-0.2.20 GEMM thread scheduling mentioned above).

Perhaps a good starting point would be 9e4b697 - a few months after 0.2.19, and shortly before both a big LAPACK update (with risk of collateral damage) and a set of thread safety fixes. If all is well up to that point, it should still display the good performance of 0.2.19.
Next would be something like 99880f7 , with my thread fixes in place and the LAPACK update stabilized, immediately before the first attempt at adding Zen support. (You will need the lapack Makefile fix from here on).
Then fa6a920 - a few weeks before 0.2.20, nothing significant should have changed in between, and 00c42dc, well past 0.2.20 and shortly before the rewrite of the GEMM thread scheduler.

I have finished bisecting between 0.2.19 and 0.2.20 to find the cause of the performance degradation reported in point 3 (see above) when ZGBTRF and ZGBTRS are called from within an OpenMP parallelised loop. The resulting output is:

87c7d10b349b5be5ba2936bfedb498fe4f991e25 is the first bad commit
commit 87c7d10b349b5be5ba2936bfedb498fe4f991e25
Author: Martin Kroeker martin@ruby.chemie.uni-freiburg.de
Date: Sun Jan 8 23:33:51 2017 +0100

Fix thread data races detected by helgrind 3.12

Ref. #995, may possibly help solve issues seen in 660,883

:040000 040000 9f41e2cd82dc83e84b65d32000d6341cc7e417a8 bcd37e483226009ef28ac179f7268fe419e0b73d M driver

As already reported in point 3 (see above), there was an additional performance degradation between 0.2.20 and the development version. Would you like to have the bisection results on that, too, or shall we see whether fixing the problem between 0.2.19 and 0.2.20 removes the additional degradation?

This is bad, as it means we will probably need someone more experienced with thread programming than me to improve this. :-(
(There must be millions of such people, but seeing that my PR went in unchallenged probably none of them on this project) In the worst case, we may be stuck with a decision between fast or safe code.
At least it should be only one of the two files affected by that PR that plays a role here - in an OpenMP build, blas_server_omp.c replaces blas_server.c so we should have only my changes in memory.c to worry about. As they were driven by helgrind reports I still believe they were substantially correct, but perhaps they can simply be made conditional on _ifndef OPENMP_

Given your find, it is entirely possible that the later degradation is from #1299 where I touched the same files again, nine months older but probably no wiser. (A brute force test could be to drop the 0.2.19 memory.c into current develop and see if this restores the original performance - I think it would still compile despite some intervening changes)

I have copied driver/others/memory.c from 0.2.19 to the development version and recompiled successfully. This has restored the good performance observed in 0.2.19.

I have now merged a (supposed) fix that uses all the additional locks only in multithreaded builds that do not employ OpenMP. This should restore pre-0.2.20 OpenMP performance without completely reverting the thread safety fixes, hopefully something can be done about their (probable) performance impact on pure pthreads builds in the future. (Though possibly the impact was worst with OpenMP, if the change was adding another layer of locks on top of what OpenMP already imposed)

The new version does not deliver correct results when compiled with

make TARGET=ZEN USE_OPENMP=1 BINARY=64 COMMON_OPT='-O2 -march=znver1 -mtune=znver1' FC=gfortran
make TARGET=ZEN USE_OPENMP=1 BINARY=64 COMMON_OPT='-O2 -march=znver1 -mtune=znver1' FC=gfortran PREFIX=/usr/local install

and running more than 1 thread.

Sorry. Seems I added ifdefs around a few locks that were there unconditionally in 0.2.19. Somehow none of the standard tests was able to flag this on my hardware.

Sorry, I still get wrong results.

Hmm, thanks. I had missed one spot near line 1120 that had a blas_unlock from the earlier version still commented out, hopefully this was causing it. Apart from that, there are only cases where I had to move a lock outside an if() block that uses a thread variable in the conditional - I do not see a functional difference but can duplicate the offending code block if necessary.

I have recompiled, but still get wrong results except for when only 1 thread is used.

  • Using 2 and 4 threads, I just get wrong results.
  • Using 8 and 12 threads, the computations get so far off that IEEE_DIVIDE_BY_ZERO floating-point exceptions are signalling from my own code.
  • Using 16 threads, I even got an output from somehwere in ZGBTRF or ZGBTRS:
    BLAS : Bad memory unallocation! : 64 0x7f775d046000

Hopefully, the latter message can help to locate the problem.

I have reverted the previous commit for now. While that "bad unallocation" message _does_ originate from memory.c, I do not understand how the revised version of my changes could be causing it.

Unfortunately I cannot reproduce your problem with any of the tests, not with the software I normally use.
BLAS-Tester suggests there is currently a problem with TRMV, but this is unrelated to the version of memory.c in use (and may be fallout from attempts to fix #1332)

Do you think there are any further test I could do or would you recommend to just copy driver/others/memory.c from 0.2.19 to the development version to get higher performance (despite the thread safety issues in this older version)?

I am about to upload another PR where absolutely all locking-related changes will be encapsulated in
_if(n)def USE_OPENMP_ . If that still does not work for you, some simplified testcase will be needed.

With the updated memory.c, the results are fine, but the run-time performance is as degraded as in v. 0.2.20

Weird. I believe a side-by-side comparison of the old and new memory.c will show that they are now functionally equivalent (with respect to locking) for USE_OPENMP=1. Did you do a full rebuild (starting from make clean) ?

Running cpp -I../.. -DUSE_OPENMP -DSMP on both "old" and "new" memory.c definitely leads to functionally equivalent codes with just a few shuffled lines. The only other major difference between the codes is my addition of cgroup support in get_num_procs (for #1155, see PR #1239 for the actual code change in memory.c), perhaps you could try commenting that one out as well.

Sorry for the late response! Running the suggested cpp command, the changes that I seem to see are

  • changes in pthread_mutex_(un)lock,
  • new defs of pthread_mutex_t
  • some "if (!memory_initialized) {" statements from 0.2.19 being reshuffled,
  • some blas_(un)lock being replaced by pthread_(un)mutex_lock,
  • one blas_goto_num and blas_omp_num removed

In both cpp-ed versions of memory.c #include prepocessor directives are replace by numeric codes after #.

Are you comparing the memory.c of 0.2.19 to the lastest from the as-yet unmerged PR ? There the cpp-processed files should show no replacements of blas_(un)lock by pthread_mutex_(un)lock and no new defines of pthread_mutex_t (only the definition of the alloc_lock moves up some 200 lines, but all new uses should be filtered out by the "ifndef USE_OPENMP").
The deletion of blas_goto_num and blas_omp_num is from an unrelated recent code cleanup patch.
All other lines should be the same, except for the two conditionals in blas_memory_free() swapping places - "position < NUM_BUFFERS" now being checked before position is used as an address into the memory array (from #1179 - unfortunately git blame shows the wrong info here, attributing it to the reversion of an unrelated change).

You were right. It seems I looked at the wrong version of memory.c by just taking the latest development version. Sorry for that! I have now downloaded memory.c from #1468 leading to https://github.com/martin-frbg/OpenBLAS/blob/7646974227a51a6c9adc9511593f5630f8fb59ee/driver/others/memory.c
Please confirm that this is the right version to look at. Using this version everything seems fine. I get the right results and the run-times show a slight improvement over those for openblas 0.2.19 and gcc 7.2

Referring to point 3 in the list above, the run-times for the OpenMP parallelised loop calling parallelised ZGBTRF and ZGBTRS in my code are now as follows

no. threads runtime 0.2.19+gcc7.2 [s] runtime devel+gcc8.0 [s]
1 35 31.7
4 11.7 11.7
8 8.3 8.1
12 6.1 6.1
16 6.1 5.9

In this specific example, the loop does 33 iterations with equal work load and, hence, no improvement can be expected by going from 12 to 16 threads.

Anyhow, even using gcc 8.0 and the latest development version with the PR version of memory.c does not bring the performance close to that of an i7-4770 for 1 to 4 threads (point 1 in list above). Do you think there would be any value to try ATLAS to automatically identify potentially optimal settings for Threadripper processors and, then, import these findings into openblas?

I suspect ATLAS will be sufficiently different to prevent direct import of findings, but at least it should provide some target numbers for actual performance of Threadripper zen cores. Unfortunately I am not aware of anything remotely like a simple list of "do's and don'ts" for ryzen vs haswell coding. It may make sense to try to profile your program to see where most of the time is "lost", or run some of the benchmarks for individual functions on both platforms - if it is ZGEMM, varying the the ZGEMM_DEFAULT_P,Q and/or ZGEMM_DEFAULT_UNROLL_ values from param.h may show some
effect, if it is ZSCAL or one of the other functions listed above, perhaps comparing microkernels for different cpus can provide a hint. (Not that I am at all experienced in this)

Regarding ATLAS I think it will be cumbersome to get all these settings which are sought during compilation. If I had a Ryzen system right now I would certainly try to test the gemm performance of the BLIS library with their already optimized implementation for Ryzen CPUs https://github.com/amd/blis
and check against the findings obtained from the benchmark (https://github.com/xianyi/OpenBLAS/tree/develop/benchmark) of OpenBLAS. The BLIS framework does already provide some optimized blocking parameters for these kernels ()https://github.com/amd/blis/blob/master/config/zen/bli_kernel.h. Then one could start varying these values as suggested by @martin-frbg.

@martin-frbg and @MigMuc: Thanks for the feedback! I tried to compile the last stable version of ATLAS (already a year and a half old) today. Despite using different sets of configuration parameters, I could not make it compile without a bunch of errors already popping up during the configuration phase. So, I decided to stop pursuing this thought.
I will have another look at BLIS, though my first impression of BLIS and libflame on AMD Threadripper was by far not as good as that of openblas.

Would it be of value to you to have PR #1468 tested on another NUMA system? I have access to a dual socket system with Xeon E5-2640 v4 CPUs.

  1. You'd probably need to pull from the math-atlas project here on github if you wanted a current ATLAS
  2. I have since merged #1468 as I was confident from my comparison of the preprocessed files that it does what it was supposed to do, and you confirmed that it also actually solved the performance problem. If you have cpu time to spare, a test run on another system would still be great.

I have now downloaded the latest ATLAS from math-atlas on github, but still face problems compiling it on Threadripper. So I have given up on this for the moment.

@martin-frbg: Regarding your comment on the importance of setting DTB_ENTRIES in getarch.c to get optimal results from unrolling, I have not made any changes to those parameters. I have tried to look up entries with the acronym DTB in AMD's Software Optimization Guide (http://support.amd.com/TechDocs/55723_SOG_Fam_17h_Processors_3.00.pdf) but could not find anything. What does DTB stand for?

I have started to modify a couple of files from https://github.com/xianyi/OpenBLAS/tree/develop/benchmark to include BLIS and will try to report on the findings soon.

Data Translation Buffer for mapping between virtual and physical memory addresses methinks... but all the parameters detected by getarch.c _that are actually used in the code_ appeared to be correct (my first comment above). That would seem to leave the various xGEMM_DEFAULT_ parameters from param.h, the use of prefetch (and possibly also "align") instructions in the inline assembly, and
possible differences in throughput for certain (AVX2?) instructions for experimentation.

I have done some benchmarking in develop/benchmark calling dgemm and zgemm using a single thread and linking to openblas and blis. The results shown in the figures below vary a bit between runs up to K=L=M=50. I have averaged over 100 runs until K=L=M=100, 10 runs until K=L=M=500 and 1 run until K=L=M=1000. Up until K=L=M~350 blis is 10 to 20% faster in dgemm. The zgemm performance is rather similar with blis being upt to about 3% faster over some ranges of K=L=M

dgemm_amd_tr1950x
zgemm_amd_tr1950x

It seems that a large part of the difference in performance as compared to Intel's Haswell comes from the actual AVX2 support offered by AMD. On the relevant wikipedia page (https://en.wikipedia.org/wiki/Zen_(microarchitecture)) it says "Zen supports AVX2 but it requires two clock cycles to complete AVX2 instruction compared to Intel's one". There seem to different interpretations of what AVX2 is, to say the least. I will follow @brada4's advice and compensate using the high number of cores.

Nevertheless, the above figures suggest that there is margin for improvement in the kernels implemented in openblas for zen. I have been looking a bit at param.h as suggested by @martin-frbg and compared the entries there to those of https://github.com/amd/blis/blob/master/config/haswell/bli_kernel.h as suggested by @MigMuc. To my understanding BLIS is based on GotoBLAS/OpenBLAS or at least both come from the University of Texas at Austin. So, I wonder whether there is any documentation on how to at least tentatively translate the MC, KC, NC, NR and MR parameters in bli_kernel.h to P, Q and R parameters in param.h. While the parameters in blis seem to be nothing else but block sizes (judging by the relevant papers), I have not found any documentation on P, Q and R in GotoBLAS/OpenPLAS (interestingly Goto uses MC, KC, NC, NR and MR in his paper as well). Do you have any idea about this?

  1. You could try if using one of the (micro)kernel files for earlier (non-AVX2) Intel hardware gives better performance.
  2. I do not think BLIS is derived from OpenBLAS or GotoBLAS, but both K.Goto and Xianyi were postdocs at Austin (possibly both in the group of Robert van de Geijn who features in the documentation of both packages)
  3. For P,Q,R the best hint so far was given in #1136

I have performed the following tests based on the comments and suggestions in #1136 and the observation that the L2 cache in a TR1950X is twice that of a Haswell processor:

  • doubled P: This gave very similar performance compared to the current development version.
  • doubled Q: This lead to crashes during run-time.
  • doubled Q and halved R: This also lead to crashes during run-time.
  • doubled P, halved M or N and MN: Similar performance as the current development version.
  • doubled P, doubled M or N and MN: Leads to crashes at compile time.

So, these tests were not very comprehensive. But looking at the comparison to blis, it seems the current settings in the development version are quite good. I will stop here and close the issue. Many thanks again for resolving the issues regarding thread safety and the performance of ZGBTRF and ZGBTRS in an OpenMP parallelised loop. Keep up the excellent work!

Thanks for testing. I will see if I can get hold of a Ryzen system for further experimenting in the near future. (One thing that should be easy to do is replacing the choice of dgemm_kernel_4x8_haswell.S for the DGEMMKERNEL in KERNEL.ZEN with its pre-AVX2 counterpart dgemm_kernel_4x8_sandy.S)

Please find some benchmark comparisons to an i7 4770 at the end of the post and a test with the sandybridge kernels in the next paragraph.

I have done the proposed test and replaced line 45 in KERNEL.ZEN by
DGEMMKERNEL = dgemm_kernel_4x8_sandy.S
However, this modification leads to error messages , when evaluating the accuracy of the test results during compilation:

OPENBLAS_NUM_THREADS=1 OMP_NUM_THREADS=1 ./dblat3 < ./dblat3.dat
TESTS OF THE DOUBLE PRECISION LEVEL 3 BLAS

THE FOLLOWING PARAMETER VALUES WILL BE USED:
FOR N 0 1 2 3 7 31
FOR ALPHA 0.0 1.0 0.7
FOR BETA 0.0 1.0 1.3

ROUTINES PASS COMPUTATIONAL TESTS IF TEST RATIO IS LESS THAN 16.00

RELATIVE MACHINE PRECISION IS TAKEN TO BE 2.2D-16

DGEMM PASSED THE TESTS OF ERROR-EXITS

* FATAL ERROR - COMPUTED RESULT IS LESS THAN HALF ACCURATE *
EXPECTED RESULT COMPUTED RESULT
1 0.162772 0.150287
THESE ARE THE RESULTS FOR COLUMN 1
* DGEMM FAILED ON CALL NUMBER:
5512: DGEMM ('N','N', 1, 31, 2, 1.0, A, 2, B, 3, 0.0, C, 2).

DSYMM PASSED THE TESTS OF ERROR-EXITS

* FATAL ERROR - COMPUTED RESULT IS LESS THAN HALF ACCURATE *
EXPECTED RESULT COMPUTED RESULT
1 0.710259 0.511848
THESE ARE THE RESULTS FOR COLUMN 1
* DSYMM FAILED ON CALL NUMBER:
418: DSYMM ('R','U', 1, 31, 1.0, A, 32, B, 2, 0.0, C, 2) .

DTRMM PASSED THE TESTS OF ERROR-EXITS

DTRMM PASSED THE COMPUTATIONAL TESTS ( 2592 CALLS)

DTRSM PASSED THE TESTS OF ERROR-EXITS

* FATAL ERROR - COMPUTED RESULT IS LESS THAN HALF ACCURATE *
EXPECTED RESULT COMPUTED RESULT
1 0.106893 0.471354
THESE ARE THE RESULTS FOR COLUMN 9
* DTRSM FAILED ON CALL NUMBER:
830: DTRSM ('R','U','N','U', 1, 31, 1.0, A, 32, B, 2) .

DSYRK PASSED THE TESTS OF ERROR-EXITS

* FATAL ERROR - COMPUTED RESULT IS LESS THAN HALF ACCURATE *
EXPECTED RESULT COMPUTED RESULT
1 0.413231 0.209279
THESE ARE THE RESULTS FOR COLUMN 1
* DSYRK FAILED ON CALL NUMBER:
1732: DSYRK ('U','N', 31, 2, 1.0, A, 32, 0.0, C, 32) .

DSYR2K PASSED THE TESTS OF ERROR-EXITS

* FATAL ERROR - COMPUTED RESULT IS LESS THAN HALF ACCURATE *
EXPECTED RESULT COMPUTED RESULT
1 -0.160742 -0.298766
THESE ARE THE RESULTS FOR COLUMN 1
* DSYR2K FAILED ON CALL NUMBER:
1732: DSYR2K('U','N', 31, 2, 1.0, A, 32, B, 32, 0.0, C, 32) .

END OF TESTS
OPENBLAS_NUM_THREADS=1 OMP_NUM_THREADS=1 ./cblat3 < ./cblat3.dat
OPENBLAS_NUM_THREADS=1 OMP_NUM_THREADS=1 ./zblat2 < ./zblat2.dat
OPENBLAS_NUM_THREADS=1 OMP_NUM_THREADS=1 ./zblat3 < ./zblat3.dat
rm -f ?BLAT3.SUMM
OMP_NUM_THREADS=2 ./sblat3 < ./sblat3.dat
rm -f ?BLAT2.SUMM
OMP_NUM_THREADS=2 ./sblat2 < ./sblat2.dat
OMP_NUM_THREADS=2 ./dblat3 < ./dblat3.dat
TESTS OF THE DOUBLE PRECISION LEVEL 3 BLAS

THE FOLLOWING PARAMETER VALUES WILL BE USED:
FOR N 0 1 2 3 7 31
FOR ALPHA 0.0 1.0 0.7
FOR BETA 0.0 1.0 1.3

ROUTINES PASS COMPUTATIONAL TESTS IF TEST RATIO IS LESS THAN 16.00

RELATIVE MACHINE PRECISION IS TAKEN TO BE 2.2D-16

DGEMM PASSED THE TESTS OF ERROR-EXITS

* FATAL ERROR - COMPUTED RESULT IS LESS THAN HALF ACCURATE *
EXPECTED RESULT COMPUTED RESULT
1 0.162772 0.150287
THESE ARE THE RESULTS FOR COLUMN 1
* DGEMM FAILED ON CALL NUMBER:
5512: DGEMM ('N','N', 1, 31, 2, 1.0, A, 2, B, 3, 0.0, C, 2).

DSYMM PASSED THE TESTS OF ERROR-EXITS

* FATAL ERROR - COMPUTED RESULT IS LESS THAN HALF ACCURATE *
EXPECTED RESULT COMPUTED RESULT
1 0.710259 0.511848
THESE ARE THE RESULTS FOR COLUMN 1
* DSYMM FAILED ON CALL NUMBER:
418: DSYMM ('R','U', 1, 31, 1.0, A, 32, B, 2, 0.0, C, 2) .

DTRMM PASSED THE TESTS OF ERROR-EXITS

DTRMM PASSED THE COMPUTATIONAL TESTS ( 2592 CALLS)

DTRSM PASSED THE TESTS OF ERROR-EXITS

* FATAL ERROR - COMPUTED RESULT IS LESS THAN HALF ACCURATE *
EXPECTED RESULT COMPUTED RESULT
1 0.106893 0.471354
THESE ARE THE RESULTS FOR COLUMN 9
* DTRSM FAILED ON CALL NUMBER:
830: DTRSM ('R','U','N','U', 1, 31, 1.0, A, 32, B, 2) .

DSYRK PASSED THE TESTS OF ERROR-EXITS

* FATAL ERROR - COMPUTED RESULT IS LESS THAN HALF ACCURATE *
EXPECTED RESULT COMPUTED RESULT
1 0.413231 0.209279
THESE ARE THE RESULTS FOR COLUMN 1
* DSYRK FAILED ON CALL NUMBER:
1732: DSYRK ('U','N', 31, 2, 1.0, A, 32, 0.0, C, 32) .

DSYR2K PASSED THE TESTS OF ERROR-EXITS

* FATAL ERROR - COMPUTED RESULT IS LESS THAN HALF ACCURATE *
EXPECTED RESULT COMPUTED RESULT
1 -0.160742 -0.298766
THESE ARE THE RESULTS FOR COLUMN 1
* DSYR2K FAILED ON CALL NUMBER:
1732: DSYR2K('U','N', 31, 2, 1.0, A, 32, B, 32, 0.0, C, 32) .

END OF TESTS
OMP_NUM_THREADS=2 ./cblat3 < ./cblat3.dat

and much later

cblas_dgemm PASSED THE TESTS OF ERROR-EXITS

* FATAL ERROR - COMPUTED RESULT IS LESS THAN HALF ACCURATE *
EXPECTED RESULT COMPUTED RESULT
1 0.246126 0.206295
THESE ARE THE RESULTS FOR COLUMN 1
* cblas_dgemm FAILED ON CALL NUMBER:
2515: cblas_dgemm ( CblasColMajor, CblasNoTrans, CblasNoTrans,
1, 9, 2, 1.0, A, 2, B, 3, 0.0, C, 2).
* cblas_dgemm FAILED ON CALL NUMBER:
1: cblas_dgemm ( CblasRowMajor, CblasNoTrans, CblasNoTrans,
1, 1, 1, 0.0, A, 2, B, 2, 0.0, C, 2).

* FATAL ERROR - TESTS ABANDONED *

Just as a comparison on DGEMM and ZGEMM benchmarks for the TR1950X using Haswell kernels an an i7-4770 (Haswell processor) using 1 thread only (and DGEMM additionally for the TR1950X using 16 threads to demonstrate how well the TR1950X scales with the number of threads):
dgemm_amd_tr1950x
zgemm_amd_tr1950x
All results were computed with the latest development version of OpenBLAS and a gcc v.8 snapshot from last week.

@tkswe88 Hi, I recently built an experimental threadripper 1950x based server with 4GPUs and 128gb ddr4@2666. But I found the linear algebra performance really bad with both numpy and Pytorch (Magma). Even if I switch from mkl to openblas, the linear algebra performance on 1950x is still slower than even i7 6700k... Before we discard the threadripper from our server, I would like to ask if there is any way now (or wait a month or so) to make threadripper perform about 80% the speed compare to i9 7900k. My impression is that linear algebra like matrix multiplication and matrix factorization is not threadripper good at for the moment. And the architecture might be too strange for openblas too. but I would like to hear more from experts. Thanks for your help in advance! The specs looked too good that we forgot there was also mkl issues. :p Further we would like to confirm if EPYC cpus have similar issues.

Are you saying OpenBLAS on threadripper is slower than MKL on threadripper?

@brada4 Thanks for response! Sorry about the confusion, I didn't mean openblas is slower than mkl on 1950x. While openblas is better than mkl on 1950x, my benchmark shows the 1950+openblas is not as good as i7-6700k +mkl. To be fair, I didn't compile openblas for 1950x given the low performance. The purpose of my question is to consult the experts and figure out what I should expext from 1950x based on the current openblas developing cycle. If 1950x can achieve at least 80% of the linear algebra performance of 7900x, we would keep it. otherwise, we would like to switch to Intel until openblas catches up. At this moment this CPU is a huge bottleneck in my system, even for preprocessing to prepare the data for my GPUs. While in general preprocessing may not necessarily use linear algebra, like svd, we do not want to exclude this as an option. Overall Threadripper is a good concept convinced us to use slower memory, slower single thread performance to exchange for a parallel processing power. We hoped that it can work. But it seems the software is still far from there. Also I realize the AVX256 splitting, AVX 512 missing, cache architecture change and memory controller scheme may pose further challenge for fast dense linear algebra optimization given my experience in optimizing blas many years ago. So I'd be happy to hear something from you guys. :)

Current OpenBLAS will essentially treat both CPUs as Haswell, it is unclear if performance differences (beyond differences in clock frequency) arise from AVX2 limitations in the Zen architecture or other effects. Depending on which functions you are comparing, and what matrix sizes you are using, it is also very probable that you are seeing fundamental differences in performance between MKL and OpenBLAS that are not specific to the AMD hardware.

@yubeic Your question is far too general to be answered in a satisfactory and fair way. There are many different factors entering for which it is very difficult to guess how they could affect performance on your system. Nevertheless, I try to answer this in a point by point:

1) numpy and Pytorch (Magma): I code in Fortran2008, so I cannot help you here.

2) mkl and openblas: OpenMP or thread parallelism with mkl routines seems fine, but support of the AVX(2) units in the TR1950X by mkl may be a problem (I only have a version from 2017, so no idea whether this was improved in a 2018 version of mkl). In my opinion openblas is the best tuned linear algebra package for the TR1950X at the moment. For some lapack routines, however, the openblas routines do not seem to be OpenMP parallelised or vectorised, which quite heavily impacts performance (one example is Cholesky factorization using DPPSV. I just replaced this by a conversion to general format using DTPTTR and factorization using DPOSV and everything was good). Note that this is not TR1950X specific, but applies to all CPUs. So looking into this a bit for the LAPACK and BLAS routines you use may have a huge impact. @brada4 and @martin-frbg may want to correct me here.

3) i7 6700k: I can imagine that the single-thread to four-thread performance on this system for highly vectorized loads (worst case DGEMM) is about slightly better than that of the i7-4770 that I used for comparison. Nevertheless, if the vector units limit you, you can always just use more threads of the TR1950X to outperform the i7 6700k or just use your GPUs (a sufficiently good GPU should also outperform an i9 7900x in highly vectorial loads). However, by far not all parts of an application are vectorial. So, you better look at the total runtime, which also depends on bandwidth. For my main application, I reach the break even point at about 5 to 5.5 TR1950x cores, when aiming for the same total run-time as using four i7-4770 cores. Of course, this varies also a bit with the problem size.

4) UMA vs. NUMA (not reported in this thread previously, I think): This is not one of your points but can boost performance by 20% per cent in some cases. The standard setting for the TR9150X is UMA. Unfortunately, this cannot be changed to NUMA in the BIOS of at least ASUS mobos and one needs a Windows installation to use the AMD tool. Nevertheless, switching to NUMA, ensuring thread affinity with the appropriate OpenMP settings and running on four threads of one CCX gave a performance boost of 20% in a part of my standard computations that is highly bandwidth dependent (i.e. where I need to access many different arrays and structures to assemble and factorise complex valued matrices) and did not change the performance in other parts of the code. At 8 cores on two CCX, NUMA is still about 10% faster than UMA for this specific part of my code. Anyhow, you would have to test this. It really depends on the code, how well it is parallelised and how rigorously the first-touch principle was or could be followed. One may argue that having a NUMA architecture on a workstation is a drawback, because a bit of care is required in code design. I chose the TR1950X deliberately because of this feature. The bigger machines that I have access to are NUMA. So testing the NUMA performance on my workstation seems to be a good idea to me.

5) i9-7900X: I do not have access to such a system for comparison.

Again, your question is too general to answer.

My TR1950X has been running stably and reliably thus far. So, I am quite happy with it. Sure, when it comes to single-threaded AVX2 workloads, it is slower than some Intel offerings, but I guess it is vectorisable work loads that you must have bought those 4 GPUs for, right?

@tkswe88 you are 99.9% correct

  1. MKL is not tuned for AMD at all... It uses SSE2 at best....
  2. It is BIOS defect.... Worth asking ASUS, it is not only openblas suffering. (And between the lines the approproate OMP settings are unlikely to affect OpenBLAS in proper way)

@brada4 on point 4:
The absence of the UMA/NUMA switch in the bios seems to be deliberate. Whatever OpenMP setting regarding thread affinity and number of threads I make, OpenBLAS seems to honour that

@tkswe88 Thanks a lot for your patient answer and I was surprised how detailed it was. I have played with 1950x for a while and did more benchmark. My current conclusion is that the software is still behind:

  1. openBLAS was release before threadripper release.
  2. Several tool chains including numpy, pytorch, tensorflow all have a similar issue when it comes to a faster prototyping of our models.
  3. Blis is not there yet either.

I feel that with some more effort maybe threadripper will work as well as an Intel system. But my time is seriously limited. :p I will definitely try AMD system once the software is ready. We had a discussion in the lab and all think the openBLAS project is a very important project. We wish more funding will be provided to support this line of research and development.

So I will switch to an i9-7960x setup instead to avoid too much tuning and all of the servers (specifically for machine learning) in our lab will be built with Intel platform for the moment.

Also huge thanks to @brada4 @martin-frbg, the comments are very informative.

@yubeic

  1. You have to compile github development version. 0.2.20 pre-dates threadripper, zen, and skylake.
    OPENBLAS_CORETYPE=Haswell may fix up recent pre-packaged binaries.
  1. mind to share it is OSX or Windows? Are you 100% sure all else is wrong but your coding?

@brada4 I only did some very basic benchmarks, which contains only matrix-vector multiplication, matrix-matrix multiplication, SVD, Symmetrix matrix eigendecomposition etc. All of our system are Ubuntu 16.04.2-16.04.4 LTS. In my benchmarks, I tried many different preoblem sizes with both double and single precision in both numpy and pytorch. I think the code is fair but I'm almost sure I didn't compile it correctly. I didn't even compile Pytorch with openblas due to a time limit, so it runs much slower than openblas numpy on threadripper. I figured it probably uses mkl natively from the current official channel. I will try my best to benchmark more according to your suggestion. I do have to say that it might be better if AMD can provide some good tutorial on how to achieve a competitive scientific computing performance on these high end CPUs. But my peers think this is a too small market, which is reasonable. It doesn't look like there is much an effort to make threadripper great for scientific computing from the software side. We were seriously thinking about building more AMD based servers given the amazing specs. It's too bad that I don't have much more time to investigate this. However, the other theoriests don't even bother trying to do something like this .. my peers were all waiting to see my results. :thinking:
AMD is the hardware level, we are at the application level, the programming interface gap is daunting. That's why I started my question with whether there is a hope to achieve 80% of 7900x performance on linear algebra with mild effort, if so it will make me more comfortable. I have to say my quick impression is not positive.. I will try to make a benchmark comparison between i9 7960x and 1950x later.

Btw, how far is a Threadripper optimized openBLAS to be released? I think a lot of people wonder if 1950x + openblas is almost equivalent to i9+7960+mkl when it comes to linear algebra. BLAS really should be Threadripper's territory from a first look. The only few benchmarks out there look really bad.

Please be aware that OpenBLAS is a volunteer effort with little and sporadic outside funding. I am still not aware of any "cheat sheet" type document that tells exactly what to change in existing "Intel-biased" assembly to achieve comparable performance on AMD.

LTS ubuntu is unlikely to get any upgrades OpenBLAS past what was shipped with distribution (0.2.18 ->0.3.0). Enterprise grade support is not orchestrated from this "upstream source" btw.

Please confirm badness with at least one of following:

Ubuntu pre-packaged 0.2.18 will work (better) with ZEN CPUs setting OPENBLAS_CORETYPE=Haswell

You can use develop version from github (if no git or svn at hand just use green button to download .zip)
https://github.com/xianyi/OpenBLAS/wiki/faq#debianlts

@martin-frbg I understand and that's why I think some cooperation should seriously stand behind this project, especially now tensor computation is increasingly more important. The libraries and programming models are too important to ignore, which is true for both Vega and Ryzen.

@brada4 This sounds interesting and I will play with it tomorrow. Thanks guys!

@brada4 What's the 'best' compilation configuration and environment setting for Threadripper 1950x? I will use the develop branch and compile it again with the optimal configuration. Then I will benchmark more and report back the numbers.

I currently just compile openBLAS from the develop branch and set the environment variable to: OPENBLAS_CORETYPE=Haswell and OPENBLAS_NUM_THREADS=16. Please let me know if there is wrong, thanks!

Also I might compile numpy with Blis to compare further.

One was enough. I dont see numeric output from you...
Visible slowness is like 10x regression which I doubt

@brada4 I was able to compile numpy (newest stable release) with python 2.7.12 shipped with Ubuntu 16.04.4. But Anaconda python keep complaining about some unrecognized setuptool commands. Along the way, I found the compilation is need for many different packages including CuPy, PyTorch, Torchvision, scipy, and many other packages ...

Once the bugs are fixed, numeric results follow. Anyway, here I got a few early numbers. Comparison: Platform1: i7-6700K 4.2G + mkl, 4 threads, RAM: DDR4-2400 64GB, Dual Channel
Platform2: 1950x 3.85G + openBLAS, 16 threads, OPENBLAS_CORETYPE=HASWELL (might worth trying EXCAVATOR too for dgemm), RAM: DDR4-2666 128GB, Quad Channel

Task 1: Large Matrix MM, size 20000 x 20000, double precision:
P1: 36.04sec
P2: 22.70sec

Taks 2: Medium Matrix Eigendecomposition, 4000x4000, double precision:
P1: 21.61sec
P2: 35.24sec

Overall, the performance on 1950x has improved quite a bit, especially the matrix decomposition. But since openBLAS uses netlib's LAPACK, I'm not sure if the poor factorization performance comes from that side or the binding.

But these tasks are quite memory bound, especially for the first one. A more systematic benchmark report will be provided when I get some more time this week, also with Blis 0.95. I'm curious about the results. My i9-7960x system will be ready soon. Once it's up, I will include some benchmark from it too.

None is memory bound for large matrix sizes (rule of thumb 10 flops can be done per memory IO, it is memory bound for very small parameter sizes)

_GEMM (P1) reads/writes O(n^2) memory while doing O(n^3) floating ops.
_GE__ (P2) among others runs O(n^1.x) _GEMM-s, thus a bit above O(n^3) in CPU and O(n^2) in memory.
There are dozen of eigenvalue algorithms in LAPACK, probably MKL uses somehow better than called one for particular input matrix (size). OpenBLAS cannot influence lapack and overload calls at that high level.

During branching ZEN it was found that haswell template performs better right away than excavator.
Since you built dynamic arch library, you can try tests on the spot.

What we found examining numbers is that OpenBLAS performs reasonably well.
Anaconda's OpenBLAS (again not orchestrated from this project) is called "nomkl", it needs coretype set to haswell.

@brada4 Thanks for sharing the information. I found 1950x's performance is very volatile. Part of the reason might be that 1950x drops its speed to about 2.1GHz by default. I have to fix some BIOS feature to give a more stable result.

@yubeic
For some reason your latest benchmarks do not seem to show on the github page. Nevertheless, I did some benchmarking in python, following https://stackoverflow.com/questions/29559338/set-max-number-of-threads-at-runtime-on-numpy-openblas to ensure that I used the right openblas library.
I tested different numbers of threads and got the following results on my TR1950X (3.6 GHz core speed, 2666 MHz quad-channel DRAM, NUMA setup):

16 threads:
20Kx20K,MM:--- 40.8606879711 seconds ---
10Kx10K,MM:--- 5.248939991 seconds ---
8Kx8K,MM:--- 2.93858385086 seconds ---
4Kx4K, MM: --- 0.43017911911 seconds ---
4Kx4K, Eig:--- 30.7862281799 seconds ---
1Kx1K, MM: --- 0.0127520561218 seconds ---
1Kx1K, Eig:--- 0.770863056183 seconds ---

8 threads:
20Kx20K,MM:--- 79.3266539574 seconds ---
10Kx10K,MM:--- 9.90933585167 seconds ---
8Kx8K,MM:--- 5.13482499123 seconds ---
4Kx4K, MM: --- 0.669865846634 seconds ---
4Kx4K, Eig:--- 28.7336189747 seconds ---
1Kx1K, MM: --- 0.0174260139465 seconds ---
1Kx1K, Eig:--- 0.674449920654 seconds ---

4 threads:
20Kx20K,MM:--- 154.140152931 seconds ---
10Kx10K,MM:--- 19.7171549797 seconds ---
8Kx8K,MM:--- 9.97354102135 seconds ---
4Kx4K, MM: --- 1.27706003189 seconds ---
4Kx4K, Eig:--- 31.2966649532 seconds ---
1Kx1K, MM: --- 0.0288860797882 seconds ---
1Kx1K, Eig:--- 0.646577119827 seconds ---

I am somewhat puzzled that the 20k*20k matrix multiplication on my system is even worse than on yours. Anyhow, it is clear that the lapack/openblas routine for eigen decomposition called from python is not very well parallelised. Here, it is really important to pick the right one (see my previous comment, @martin-frbg and @brada4 may help).

Interestingly, I had to use some tuning of OpenMP parameters (thread affinity)
export OMP_DISPLAY_ENV='true'
export OMP_PROC_BIND='close'
export OMP_PLACES='{0,16},{1,17},{2,18},{3,19},{4,20},{5,21},{6,22},{7,23},{8,24},{9,25},{10,26},{11,27},{12,28},{13,29},{14,30},{15,31}'
to get these results.
Without these settings using 16 threads, I got the following
20Kx20K,MM:--- 49.4122049809 seconds ---
10Kx10K,MM:--- 6.79072284698 seconds ---
8Kx8K,MM:--- 2.77755713463 seconds ---
4Kx4K, MM: --- 0.380570888519 seconds ---
4Kx4K, Eig:--- 34.6536951065 seconds ---
1Kx1K, MM: --- 0.0201449394226 seconds ---
1Kx1K, Eig:--- 0.839740037918 seconds ---
as the best result.

Generally, I would recommend to narrow down the comparison to the i7-6700 by using openblas on that system, too. Otherwise, you will have difficulty understanding what parts of the runtime differences are related to the architectures (TR1950X vs i7-6700) and the libraries (mkl and openblas).

When you suspect thermal throttling to be a problem, it would be helpful to monitor core speeds. I use an NH-U14S TR4-SP3 air cooler and can run stably at 3.6 and 3.75 GHz without throttling it seems.

If you have heat dissipation problems you cannot expect any performance at all. Fix thermal setup first.
Can you check with numactl -H if NUMA table is set up correctly , i.e. two NUMA pseudonodes. Failure here is of mobo maker, and costs you in range 10-20% RAM bandwidth in average case, like 80-90% in worst case.

@tkswe88 @brada4 Yesterday the number was quite volatile, partially due to I didn't turn of the CPU feature Quite 'n Cool. This feature dials down the clock speed very often. After I turned it off, now the performance is much more stable. Here is the number report again:

Platform 1: i7-6700K 4.2G + MKL, DDR4-2400 64GB Dual Channel
30Kx30K,MM:--- 121.266254902 seconds ---
20Kx20K,MM:--- 36.3553829193 seconds ---
10Kx10K,MM:--- 4.55528593063 seconds ---
8Kx8K,MM:--- 2.26043701172 seconds ---
4Kx4K, MM: --- 0.312715053558 seconds ---
4Kx4K, Eig:--- 21.2994120121 seconds ---
1Kx1K, MM: --- 0.00685691833496 seconds ---
1Kx1K, Eig:--- 0.343578100204 seconds ---

Platform2: i7-6850K 3.6G + MKL, DDR4-2400 64GB Quad Channel
20Kx20K,MM:--- 27.84059715270996 seconds ---
4Kx4K, MM: --- 0.3708188533782959 seconds ---
4Kx4K, Eig:--- 21.92760944366455 seconds ---
1Kx1K, MM: --- 0.006255626678466797 seconds ---
1Kx1K, Eig:--- 0.40999412536621094 seconds ---

Platform3: 1950x 3.8G + openBLAS, DDR4-2666 128GB Quad Channel
30Kx30K,MM:--- 69.3856248856 seconds ---
20Kx20K,MM:--- 22.5743000507 seconds ---
10Kx10K,MM:--- 3.56968283653 seconds ---
8Kx8K,MM:--- 2.06472492218 seconds ---
4Kx4K, MM: --- 0.443926095963 seconds ---
4Kx4K, Eig:--- 31.6315670013 seconds ---
1Kx1K, MM: --- 0.00886201858521 seconds ---
1Kx1K, Eig:--- 0.971400976181 seconds ---

I included an even larger matrix 30Kx30K. It seems the performance of 1950x with large matrices is almost 2x of the performance of i7. This really worries me that the speed gain might mainly come from the memory bandwidth. The MM performance on my machine is generally much faster (except for the 4Kx4K one) than @tkswe88's performance, but my SVD performance is worse. But I agree the the scalibility of SVD is an big issue. I just followed the general formula to compile numpy with openBLAS with the site.cfg file. I also included some numbers from an i7-6850k platform since it also has quad channel, it seems it only outperform the 6700k platform when matrices get large. Even not overclocked, assuming a linear scalability, it should still outperform the 6700k. I'm confused by these results now.

One thing about the eigendecomposition is that though 16 threads are running, the CPU utilization doesn't seems to be very high, the following is the utilization curves of each of the cores during 4Kx4K eigendecomposition:

utilization

The heat might be an issue, but given I only have a Corsair H100i v2 for this CPU, I can only turn down the OC to make things better. I wasn't able to install the thermal sensor chip driver for this mobo, so currently I can not measure the internal temperature exactly. The cooler's sensor reads fine, below 58 C.

When it comes to matrix factorization and small matrices, i7 platform still wins by a non-trivial margin. I printed NUMA info here:
available: 1 nodes (0)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
node 0 size: 128806 MB
node 0 free: 122155 MB
node distances:
node 0
0: 10
This doesn't seems right. Do you know how to fix it? @brada4

Next I compiled numpy with BLIS (0.95Beta MT version from AMD website) on 1950x, and set BLIS_NUM_THREADS=16. The results are .. abysmal .. which makes me believe I must compiled it in a very dumb way:
30Kx30K,MM:--- 209.907198191 seconds ---
20Kx20K,MM:--- 62.3285088539 seconds ---
10Kx10K,MM:--- 7.86016511917 seconds ---
8Kx8K,MM:--- 4.07193017006 seconds ---
4Kx4K, MM: --- 0.58310008049 seconds ---
4Kx4K, Eig:--- 337.510659933 seconds ---
1Kx1K, MM: --- 0.0233819484711 seconds ---
1Kx1K, Eig:--- 5.63355708122 seconds ---

Numpy BLAS link info:
lapack_info:
NOT AVAILABLE
lapack_opt_info:
NOT AVAILABLE
openblas_lapack_info:
NOT AVAILABLE
atlas_threads_info:
NOT AVAILABLE
openblas_clapack_info:
NOT AVAILABLE
atlas_3_10_threads_info:
NOT AVAILABLE
lapack_src_info:
NOT AVAILABLE
blas_mkl_info:
NOT AVAILABLE
blas_opt_info:
language = c
define_macros = [('HAVE_CBLAS', None)]
runtime_library_dirs = ['/home/yubeic/SysBoost/blis/lib']
libraries = ['blis', 'blis']
library_dirs = ['/home/yubeic/SysBoost/blis/lib']
include_dirs = ['/home/yubeic/SysBoost/blis/include/blis']
blis_info:
language = c
define_macros = [('HAVE_CBLAS', None)]
runtime_library_dirs = ['/home/yubeic/SysBoost/blis/lib']
libraries = ['blis', 'blis']
library_dirs = ['/home/yubeic/SysBoost/blis/lib']
include_dirs = ['/home/yubeic/SysBoost/blis/include/blis']
atlas_info:
NOT AVAILABLE
atlas_3_10_info:
NOT AVAILABLE
lapack_mkl_info:
NOT AVAILABLE

I think there is no parallel LAPACK linked, so the eigendecomposition numbers should be ignored. I have to say it's not that easy to get these working ..

Choose the best option that works for you?
You must contact BIOS vendor to set NUMA properly or try fake numa:
http://linux-hacks.blogspot.com.es/2009/07/fake-numa-nodes-in-linux.html
(It was seen that threadripper is not numbered in order like intel, so it might be tough to get right conf for +20% perf bonus)
In case you get NUMA right you may try smaller samples on half CPU aka numa node.

Result should look something like this (this is a virtual machine with all numbers round and beautiful):

$ numactl -H
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11
node 0 size: 64511 MB
node 0 free: 41721 MB
node 1 cpus: 12 13 14 15 16 17 18 19 20 21 22 23
node 1 size: 62464 MB
node 1 free: 45089 MB
node distances:
node   0   1
  0:  10  20
  1:  20  10

i.e reflect 2 CPU halves with independent memory channels, your cores will not be in counting order.

@yubeic To switch between NUMA and UMA you can try two options:
1) in the bios, if the mobo manufacturer provides this functionality (the ASUS Prime X399-A mobo I use does not provide this).
2) AMD Ryzen Master Utility which is only for Windows. I used this option even though I had to beg our a sysad for a hard disk to install Windows on.
To me it does not seem a good idea to fake NUMA nodes as @brada4 suggested. The TR1950X is naturally a NUMA architecture and the UMA functionality provided by AMD is just an emulation. So faking NUMA nodes in a UMA emulation is faking a fake in a sense.

I am unsure what thread numbering problem @brada4 refers to, but I always get this numbering using numactl -H:

available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23
node 0 size: 32117 MB
node 0 free: 25690 MB
node 1 cpus: 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31
node 1 size: 32221 MB
node 1 free: 27757 MB
node distances:
node 0 1
0: 10 16
1: 16 10

When limiting jobs to run on 4 threads and taking care to have affinity of these 4 threads to just one CCX units (which has 4 cores), I get somewhat (20% in some cases) improved performance on bandwidth limited operations. The improvements are consistently lower when using 8 specific cores (distributed over two CCX that is) and vanish when I do not care about thread affinity.

In short, the threads are distributed over the CCX as
CCX0 0 1 2 3 16 17 18 19
CCX1 4 5 6 7 20 21 22 23
CCX2 8 9 10 11 24 25 26 27
CCX3 12 13 14 15 28 29 30 31
So getting the numbering for thread affinity right is quite simple.

@tkswe88: yup, same 20% i am talking about.
i think if fake numa can be set still @yubeic must cross-check performance results, because I have some doubts about that conf - if BIOS set memory to interleaved mode he will not get full NUMA, just some partial effect from affining CPU caches closer to what is on the chip, and the last crumble for performance should be squeezed from mobo vendor shipping proper NUMA config in BIOS/UEFI

@brada4 @tkswe88 I checked the motherboard, MSI X399 Carbon has an option to set memory interleaving to channel, which should switch from UMA to NUMA. Some discussion says it doesn't work properly, I will try and test.

you must compare simple (gemm) timings with @tkswe88 , 20% difference means it does not work properly.
you need memory module in each channel, usually motherboard guide says about RAm slot population order. 4 channels means 4+ RAM modules....

@brada4 I'm able to get the NUMA setting to:
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23
node 0 size: 64307 MB
node 0 free: 51021 MB
node 1 cpus: 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31
node 1 size: 64498 MB
node 1 free: 51840 MB
node distances:
node 0 1
0: 10 16
1: 16 10
But the performance of the benchmark becomes worse than UMA, which is really confusing. And this change somehow also makes other applications to have a periodic large latency. There are other options of memory interleaving rather than channel.

I just did another benchmark on i9-7960x + mkl (default version shipped with Anaconda, MKL 2018.0.2). The following is the result:

Platform 4: 7960x 2.8G + MKL 2018.0.2, DDR4-2666 128GB Quad Channel
30Kx30K,MM:--- 34.8145999908 seconds ---
20Kx20K,MM:--- 11.1714441776 seconds ---
10Kx10K,MM:--- 1.65482592583 seconds ---
8Kx8K,MM:--- 0.960442066193 seconds ---
4Kx4K, MM: --- 0.162860155106 seconds ---
4Kx4K, Eig:--- 17.041315794 seconds ---
1Kx1K, MM: --- 0.00704598426819 seconds ---
1Kx1K, Eig:--- 0.410210847855 seconds ---

After increase VCPUIN voltage from about 1.8V to 2.0V, the result is roughly:
30Kx30K,MM:--- 26.9505310059 seconds ---
20Kx20K,MM:--- 8.86561799049 seconds ---
10Kx10K,MM:--- 1.42309403419 seconds ---
8Kx8K,MM:--- 0.801309108734 seconds ---
4Kx4K, MM: --- 0.151351213455 seconds ---
4Kx4K, Eig:--- 15.6785330772 seconds ---
1Kx1K, MM: --- 0.00755000114441 seconds ---
1Kx1K, Eig:--- 0.390221118927 seconds ---

so Threadripper is half as fast as a 7960x. is that about as good as it will ever get?

All ZEN re-uses haswell code, which was measured better than excavator, there is nothing yet specifically tuned for ZEN. I dont see anything unfair in double speed for double price for now.

the "periodic large latency" comes from forced process migration between numa nodes. Use one numa node and it will perform better than UMA 8 cores.

Just a few comments:

@yubeic : When using NUMA, you have to make sure that the threads and the associated data (first touch principle applies) start moving across the UMA and CCX nodes by ensuring thread affinity. When using only four threads and making sure that they stay on the same CCX, the performance has to be better than on 4 threads in UMA. So this is basically the same advice as @brada4 has given.

@yubeic and @LukeSBE : The TR 1950X has two 128bit AVX FMA units, whereas the i9-7960X has two AVX-512 FMA units. Given the heat throttling that is used in Intel processors in AVX 512 mode, one might have expected the i9-7960X to be about a factor of three faster than the TR1950X at the same clock speed. Taking into the clock speeds of 3.8 GHz (TR) and 2.8 GHz (i9; Did the i9 run at 2.8GHz using AVX-512 on all 16 cores? ), the TR 1950X does slightly better than I expected in a comparison that emphasises the vector units. This may (or may not) be related to the larger L2 cache in the TR1950X. In less vectorial workloads the TR 1950X can be as fast as the i9-7960X (or possibly a little faster) depending on the case. So the question is: Do you multiply matrices and vectors all day long? If so and money does not matter, the Intel processors may be the better choice. However, using GPUs may seem even more appropriate.

@tkswe88 regarding taskset - on modern Linux kernels some rudimentary numa node balancer exists, so in principle after few warmup rounds your numa-node or less sized process is in a good placement.
e.g. check the configuration of balancer in kernel running:

grep NUMA_BALANCING /boot/config-`uname -r`

or enable now and on boot if respective sysctl interface is present:

sysctl kernel.numa_balancing=1 >> /etc/sysctl.conf

All the following performance should not be treated as over-clocking performance. The overclocking was misleading. Please read the article provided by @brada4 in later comments. I was trying to overclock the CPU, but it never runs at an overclocked rate during the all core numerical test.

@tkswe88 I slightly overclock 7960x to "3.8Ghz" too to make a comparison:
("3.8"GHz might be false since when all core start to run, the frequency dropped to about 3.0Ghz, so this overclocking was not successful.)

30Kx30K,MM:--- 26.9505310059 seconds ---
20Kx20K,MM:--- 8.86561799049 seconds ---
10Kx10K,MM:--- 1.42309403419 seconds ---
8Kx8K,MM:--- 0.801309108734 seconds ---
4Kx4K, MM: --- 0.151351213455 seconds ---
4Kx4K, Eig:--- 15.6785330772 seconds ---
1Kx1K, MM: --- 0.00755000114441 seconds ---
1Kx1K, Eig:--- 0.390221118927 seconds ---

The power draw at this frequency is huge, about 290watt when AVX kicks in. If the overclocked to 4.2 Ghz, 30Kx30K MM can get to about 25.6 seconds, which is really marginally improved.

@tkswe88 In fact from the information showed by turbostat, it seems once AVX512 is used on all cores, their frequency all throttled at 3.0GHz - 3.3GHz (Even the whole processor is clocked to 3.8GHz.) But given the power draw is already 290Watt, I don't think there is much room for a higher AVX frequency.

I only use this workstation for numerical simulations. Otherwise I will probably just get a 8700K. Most of my processing is send to the GPUs, but unfortunately, some of my models need eigendecomposition. The eigendecomposition performance can easily become a bottleneck if I launch more than 2 processes. At that point launch more GPU processes will slow down the running jobs. So the major problem with Threadripper was that the SVD and eigendecomposition was taking too long. Otherwise, it would still be usable.

If fact, even with this i9 CPU, svd performance is still really bad. Here is the ibench test result ("3.8Ghz", in fact, the result is really a stock performance ...):
Lu:
Lu: N = 35000
Lu: elapsed 40.091505 gflops 712.952365
Lu: elapsed 39.271447 gflops 727.840086
Lu: elapsed 38.881067 gflops 735.147863
Lu: gflops 727.840086
Cholesky:
Cholesky: N = 40000
Cholesky: elapsed 20.265417 gflops 1052.696484
Cholesky: elapsed 19.970937 gflops 1068.218948
Cholesky: elapsed 19.938883 gflops 1069.936228
Cholesky: gflops 1068.218948
Inv:
Inv: N = 25000
Inv: elapsed 37.933589 gflops 823.808151
Inv: elapsed 38.142073 gflops 819.305235
Inv: elapsed 37.802702 gflops 826.660487
Inv: gflops 823.808151
Fft:
Fft: N = 520000
Fft: elapsed 0.383207 gflops 128.831636
Fft: elapsed 0.368043 gflops 134.139768
Fft: elapsed 0.367436 gflops 134.361283
Fft: gflops 134.139768
Det:
Det: N = 30000
Det: elapsed 17.214028 gflops 1045.658801
Det: elapsed 16.712926 gflops 1077.010698
Det: elapsed 16.527369 gflops 1089.102565
Det: gflops 1077.010698
Svd:
Svd: N = 10000
Svd: elapsed 108.278476 gflops 12.313928
Svd: elapsed 108.142802 gflops 12.329377
Svd: elapsed 108.045384 gflops 12.340493
Svd: gflops 12.329377
Dot:
Dot: N = 10000
Dot: elapsed 1.473358 gflops 1357.443414
Dot: elapsed 1.455766 gflops 1373.847206
Dot: elapsed 1.454956 gflops 1374.611964
Dot: gflops 1373.847206
Qr:
Qr: N = 10000
Qr: elapsed 5.096478 gflops 261.618580
Qr: elapsed 5.080871 gflops 262.422192
Qr: elapsed 5.087934 gflops 262.057906
Qr: gflops 262.057906

It seems SVD is very hard to parallelize and I probably will avoid using all cores on SVD problems since it's not scalable and it takes a lot of resources.

You have 2 SVD algorithms in lapack: http://www.netlib.org/lapack/lug/node32.html
Depending on typical input you have one may be faster than other.
Yes, they call GEMM on small matrices, in order, depending on previous GEMM output, and OpenBLAS uses one core for small GEMM. Thats not a performance poblem, just a normal life sign of incremental generations algorithms for SVD.

If you overclock x86_64 core AVX core gets clocked down, and if you overclock even more then uncore (caches etc) clocks down. Fair comparison is with factory clocks, please.
ARM and AMD has same sort of self-regulation built in.

@brada4 I digged into it a little further and I found my overclocking multiplier setting was not effective under Linux at all. Well, I can see through turbostat that they are allowed to be 3.8GHz. But they have never actually achieved this frequency under load. The reason for the performance improvement was purely from a change of VCPUIN from 1.8V to 2.0V. 馃憥 So the above score is in fact a stock turbo boost performance. However the performance I'm getting might be even worse than the stock turbo boost performance since I have never seen the promised 4.4Ghz single core turbo boost at all, they are also bounded below 3.7Ghz at turbo. I'm not sure why the overclocking under linux is not successful, it might be a system kernel bug (say in intel_pstate driver) or motherboard firmware bug I guess. Possibly the problem is in Intel microcode, but I don't want to disable it since Skylake-X has a few critical bugs, e.g. security and hyper threading crash.

This is not overclocking forum. Your benchmark measures frequency on a single core with others idle. It is absolutely false information. Spinning all cores you get stock base frequency.
Example measurement for older CPU series:
https://www.microway.com/knowledge-center-articles/detailed-specifications-of-the-intel-xeon-e5-2600v4-broadwell-ep-processors/

@brada4 I think you are right, my overclocking wasn't successful. And I did a bunch of benchmarks using phoronix and find the scores I got are constantly below (usually 2-5%) the scores from others reported. So I'm going to delete the misleading benchmarks and only keep a stable but optimized stock frequency setting result. Also thanks for the reference, it's a good read. Once I can solve the issue, I will update the info for a fair comparison. Do you know if the AVX512 puts a hard threshold on the all core frequency? It seems that no matter what I do, AVX512 all core can not go beyond 3GHz.

This is not an overclocking forum....

I guess data from overclocked systems will still be useful as long as they are marked as such. (And unless you run the latest-and-greatest develop branch, OpenBLAS will not make any use of AVX512 yet, even with develop you will only see it used for GEMM. For AMD Zen architecture, BLIS should have a clear advantage as nobody here has worked on Zen-specifc code yet)

At some point of overclocking you start getting prominent timings while losing the correctness due to bit flips inevitably happening in overclocked components. More voltage is needed to flip semiconductor state at higher frequencies, which in turn yields Freq^2 thermal output and power consumption while you have just one air flow to chill CPU before it fries...
Your mileage may vary, and that last smell from CPU is not of pleasant and healthy ones.

Zen has been around for 16 months, and OpenBLAS has not been updated to not support it (I do not regard 50% speed per core vs Intel as supported). Is OpenBLAS being actively developed? There is a Threadripper 32 core coming out soon. It seems a waste of good hardware to have out there if no vector libraries take advantage of it!

OpenBLAS is a volunteer project with few active developers and little if any external funding. Just sitting there shouting "do something for me" is unlikely to bring any useful results.

From the results seen in https://github.com/xianyi/OpenBLAS/commit/6eb4b9ae7c7cc58af00ac21b52fed8810d7e5710 , it would probably make sense to explore increasing the SWITCH_RATE for ZEN in param.h as well. Anybody with a Ryzen or Threadripper willing to try ?

I can run a test tomorrow. Would 4, 8, 16 and 32 be meaningful test values or would you like to see other values tested?

I'd try 32 first as that seemed to fit Haswell, and then a step to both sides ?

Apologies I did not realise TR FMA was 4 times less than Skylake-X. The fact that it gets half performance shows it's pretty well supported already.

Probably need to try some kind of hybrid kernel between Haswell and Sandybridge, to see if we can avoid the relative AVX2 weakness of Zen. I do not have the hardware to try this myself at the moment however.

Here are my results on a threadripper 1900x. I've tested higher switch rates as you requested martin. ~I'm gaining a little doing so (i'm repeatedly gaining 1 sec on 20kx20k matricies) but it's not a game changer.~ (Following tests shown that those variations was in my error margin, so we should not conclude anything).

OpenBlas commit 71c6deed60c4b
Threadripper 1900X, asus prime X399, 4x8GB DDR4 2800MHz. Didn't touched to anything related to UMA/NUMA, so i suppose i'm in UMA mode.

8 threads standard config
20Kx20K,MM:--- 76.631098 seconds ---
10Kx10K,MM:--- 9.659299 seconds ---
8Kx8K,MM:--- 5.007382 seconds ---
4Kx4K,MM:--- 0.638273 seconds ---
1Kx1K,MM:--- 0.013002 seconds ---

8 threads SWITCH_RATE=32
20Kx20K,MM:--- 75.376612 seconds ---
10Kx10K,MM:--- 9.583702 seconds ---
8Kx8K,MM:--- 4.961513 seconds ---
4Kx4K,MM:--- 0.639170 seconds ---
1Kx1K,MM:--- 0.012943 seconds ---

8 threads with thread affinity (don't know why i've tested that ! but no
20Kx20K,MM:--- 76.205891 seconds ---
10Kx10K,MM:--- 10.457863 seconds ---
8Kx8K,MM:--- 4.982340 seconds ---
4Kx4K,MM:--- 0.636596 seconds ---
1Kx1K,MM:--- 0.012721 seconds ---

not inside with_thread(8)
20Kx20K,MM:--- 86.750650 seconds ---
10Kx10K,MM:--- 11.032390 seconds ---
8Kx8K,MM:--- 5.676041 seconds ---
4Kx4K,MM:--- 0.740049 seconds ---
1Kx1K,MM:--- 0.015212 seconds ---

My results and tkswe88's on 8 threads seems very close.

What i don't understand is yubeic's results on 16 cores 1950x :

20Kx20K,MM:--- 22.5743000507 seconds ---
10Kx10K,MM:--- 3.56968283653 seconds ---
8Kx8K,MM:--- 2.06472492218 seconds ---
4Kx4K, MM: --- 0.443926095963 seconds ---
4Kx4K, Eig:--- 31.6315670013 seconds ---
1Kx1K, MM: --- 0.00886201858521 seconds ---
1Kx1K, Eig:--- 0.971400976181 seconds ---

Far better than tkswe88 with the same processor, how can we explain such disparity ?

I'm ready to do some more testing if needed.

Can you show numactl -H?
Memories have to be installed on different memory channels for fake numa to do anything.

Forgot to mention i'm on linux 4.15.0-36-generic and results are obtained through numpy.

Here it is.

available: 1 nodes (0)
node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
node 0 size: 32085 MB
node 0 free: 29216 MB
node distances:
node 0
0: 10

I've juste tested TARGET=SANDYBRIDGE
I though it would be an interesting test because, similarly to zen, Sandy Bridge only has 2*128bits FMA per core (although zen has 2 more 128bits FADD) .
Results :
20Kx20K,MM:--- 136.080165 seconds ---
10Kx10K,MM:--- 17.193669 seconds ---
8Kx8K,MM:--- 8.815149 seconds ---
4Kx4K,MM:--- 1.117620 seconds ---
1Kx1K,MM:--- 0.022499 seconds ---
This is not good.

Do you have an intuition of why Haswell better handles threadripper than sandybridge ? By isolating this, we could try hybrid kernel as martin suggested.

Reopening for better visibility, but no immediate intuition. Perhaps compiler flags play a more important role than we assumed so far, default is just -O2 without any -march= setting.

With O3 , keeping everything else to default :

20Kx20K,MM:--- 75.296078 seconds ---
10Kx10K,MM:--- 9.579083 seconds ---
8Kx8K,MM:--- 4.960849 seconds ---
4Kx4K,MM:--- 0.632421 seconds ---
1Kx1K,MM:--- 0.013105 seconds ---

With O3 and SWITCH_RATE=32

20Kx20K,MM:--- 75.662260 seconds ---
10Kx10K,MM:--- 9.617999 seconds ---
8Kx8K,MM:--- 4.971955 seconds ---
4Kx4K,MM:--- 0.637424 seconds ---
1Kx1K,MM:--- 0.013046 seconds ---

I have to signal that i'm too lazy to run those experiments multiple times, so It's hard to conclude anything expect that changes are marginals; we are far from something that could explain numbers obtained by yubeic.

Next experiment : changing memory interleaving to "channel" in bios.
I'm now observing :

numactl -H
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 8 9 10 11
node 0 size: 15991 MB
node 0 free: 14926 MB
node 1 cpus: 4 5 6 7 12 13 14 15
node 1 size: 16093 MB
node 1 free: 15213 MB
node distances:
node   0   1 
  0:  10  16 
  1:  16  10 

which seems right.

But results are sensibly worse on 20k matrices:
20Kx20K,MM:--- 79.732724 seconds ---
10Kx10K,MM:--- 10.347193 seconds ---
8Kx8K,MM:--- 5.059734 seconds ---
4Kx4K,MM:--- 0.638466 seconds ---
1Kx1K,MM:--- 0.013033 seconds ---
I reran the experiment multiple times and this seems quite stable. Yubeic seems to have observed the same thing in its previous experiments

@brada4 I'm able to get the NUMA setting to:
available: 2 nodes (0-1)
node 0 cpus: 0 1 2 3 4 5 6 7 16 17 18 19 20 21 22 23
node 0 size: 64307 MB
node 0 free: 51021 MB
node 1 cpus: 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31
node 1 size: 64498 MB
node 1 free: 51840 MB
node distances:
node 0 1
0: 10 16
1: 16 10
But the performance of the benchmark becomes worse than UMA, which is really confusing. And this change somehow also makes other applications to have a periodic large latency. There are other options of memory interleaving rather than channel.

And with NUMA on (aka channel) and 8 threads (OPENBLAS_NUM_THREADS=8 ./testsuite)?
Compiler optimisations are largely indifferent since biggest part of work is done by custom assembly code.

Is your compiler getting -march=zen from its default configuration ? (Not that I expect much improvement from it, as the bottleneck is probably somewhere in the hand-coded assembly, but perhaps cpu-specific optimizer settings are more important than going from -O2 to -O3 ?)

My compiler did not get -march automatically.
I've tried with -march=znver1 (zen was not allowed) : no significant changes.

Where is testsuite located ?

What about SMT (it was activated for now), is there any recommandation ? I will test ASAP

I can only refer to Agner Fog's analysis at https://www.agner.org/optimize/microarchitecture.pdf where (as I understand it) he comes to the conclusion that multithreading is more efficient than it was on earlier AMD and Intel designs (with the caveat that inter-thread communication should be kept within the same 4-cpu die if possible). Unfortunately I cannot identify any obvious bottlenecks in the current (Haswell) kernels even after reading his description of the microarchitectures.

And I suspect brada4 was only using "testsuite" as shorthand for your current numpy code. (There are some test codes in the benchmark directory of the distribution and there is xianyi's BLAS-Tester project that is derived from the ATLAS test codes, but I am not aware of anything actually named testsuite)

@jcolafrancesco : Many thanks for the tests!

@martin-frbg and @brada4 : Sorry for not being in contact after promissing test. First, one of my RAM modules was broken. After that, I had too much teaching.

With a reservation, I can confirm @jcolafrancesco 's findings that changing the SWITH_RATE parameter has pretty marignal effect. I tested this 3 weeks ago or so with a freshly downloaded openblas version and one from spring. Here, the multi-threaded performance was worse in the recently downloaded version and basically degraded to the level, before @martin-frbg fixed issue #1468 on February 28.

Unless this description raises immediate suspicions about the degraded performance, I can download the last development version, recheck and bisect.

Also, AMD published an updated blis libflame version on their developer webpage a couple of months ago or so. I have not had the time yet to look at how much this has improved threadripper support.

@jcolafrancesco : Regarding SMT, I have not seen any performance benefits from this on the TR1950X. Going beyond 16 threads, it was rather slight performance degradation by a few per cent. Generally, I recommend that you test setting thread affinity in both UMA and NUMA model to see what works best for your specific work tasks. From what I have seen, it is beneficial to set thread affinity and avoid having your data move through the InfiniFabric between the two main parts of the CPU to different DIMM modules. For my typical workloads, it is fastest to run 2or 4 simulations in parallel on 8 or 4 cores, respectively, and just do that in NUMA.
I have never done any tests with SMT switched off in memory, because I assumed that setting the thread affinity would be sufficient to avoid adverse effects.

@tkswe88 memory.c should be basically the same as #1468 from end of february (where you confirmed that the performance was back to earlier levels) - since then, there has been a very promising rewrite of the code with the intent to use thread-local storage. Unfortunately this new code had unexpected side effects at first, so I decided to make the "old" version the default again after two consecutive releases that were "broken" for some high-profile codes that indirectly rely on OpenBLAS.
This makes the current memory.c twice as big as the original with an ugly #ifdef to switch between the two implementations at compile time.

20k * 20k * 8b * 3 makes 9.6GB for double precision... Should get better with working RAM

Yubeic also posted on AMD's website : https://community.amd.com/thread/228619

He is communicating the same results and someone is comparing them with the theoretical limit he could reach with its Threadripper (canyouseeme 23 mai 2018 04:35 ):

220k^3/22.4687=712.1GFLOPS, 3.85G8ops*16cores=492.8GFLOPS
...
if it's double precision, your result exceeds the upper limit? I'm wondering if some algorithm like strassen is used inside.

Same poster is obtaining 0.36 seconds on dgemm with 4096*4096 matrices and this is totally on par with tkswe88's results (around 0.3 seconds) and mine.

Do you think that canyouseeme's reasoning is correct ? In that case it would cast some doubts on yubeic's results. I'm starting so think that we should just stop to take those results into account.

canyouseeme is also reporting (22 mai 2018 03:04) :

ps. dgemm() run faster when OPENBLAS_CORETYPE=EXCAVATOR than HASWELL on TR 1950x.

I've made some tests with a new C code directly using the cblas API :

Target=EXCAVATOR : 
./test_cblas 1000
took 0.026141 seconds 
./test_cblas 2000
took 0.095920 seconds 
./test_cblas 4000
took 0.625256 seconds 
./test_cblas 8000
took 4.839866 seconds 
./test_cblas 10000
took 9.572862 seconds 
./test_cblas 20000
took 74.236080 seconds 

Target=ZEN : 
./test_cblas 1000
took 0.021527 seconds 
./test_cblas 2000
took 0.090566 seconds 
./test_cblas 4000
took 0.613909 seconds 
./test_cblas 8000
took 5.138182 seconds 
./test_cblas 10000
took 9.232099 seconds 
./test_cblas 20000
took 73.998256 seconds 

I'm not observing any significant change.

@martin-frbg What specific preprocessor flag is required during compilation of memory.c to restore the optimal performance?

@tkswe88 there should be no specific flag necessary - the active code in memory.c should be in the same state as on february 28, except that the file is now approximately twice as big as it also contains a reimplementation that uses thread-local storage (which is available by setting USE_TLS=1)

USE_TLS=1 ... which was accidentally enabled in Makefile.rule shortly but long enought to land in a release, and should be commented out if found.
Also for sake of experimentation you may restore older memory.c, the outside interfaces have not changed since.

what's the status of this ticket and is there a way to help it move forward?

Needs testers especially for Threadripper, and generally someone who knows how to optimize code for Zen. I just bought a Ryzen 2700 and hope to be able to do at least some quick and simple tests in the next few days. (Top on my list is to see how the recent improvements for Haswell map to Ryzen, e.g. optimal settings of SWITCH_RATIO and GEMM_PREFERED_SIZE in param.h)

Not much experience optimizing code, but I do have access to a TR1950x and can regularly run tests on it. Could you describe in detail what kind of tests would you need?

I am not that sure myself, but for a start it would probably help to run the dgemm and sgemm benchmarks to see if increasing the SWITCH_RATIO for ZEN in param.h from the default of 4 has a similar impact on performance as it did for HASWELL where it is now 32 - my very first tests suggest that 16 may be optimal for the 8-core Ryzen, but 32 did not appear to be much worse. Second task would be to see if fenrus75's recent improvements in the Haswell xGEMM_BETA and xGEMM_ONCOPY routines (see entries referencing skylake files in kernel/x86_64/KERNEL.HASWELL) are applicable to the Zen architecture despite its allegedly inferior AVX2 hardware.
Going beyond that will probably require some in-depth experience with coding to processor specs, possibly trying things like rearranging or replacing avx2 instructions. Some pointers are in
https://www.agner.org/optimize/blog/read.php?i=838 but this is a bit outside my expertise.

There is a new best practice guide from the PRACE project for AMD EPYC architectures on http://www.prace-ri.eu/IMG/pdf/Best-Practice-Guide-AMD.pdf
This might be helpful in optimising code.

Thats more about configuring node in a compute cluster. AMD does not make 3dnow processors for quite some decades....

@martin-frbg and @brada4 I have adverse performance effects on the TR1950X when using the memory.c file as provided in the current current development version (0.3.6) and runnin gon more than 8 cores. It does not seem to matter whether I define USE_TLS or not. I have alos tried commenting out lines 1045-1047 in Makefile.system. When replacing memory.c by the one of #1468, I get good performance again.
Could you give a hint on what compile time settings are required to have the preprocessor construct a from the current memory.c a version that is identical to that of #1468?
I am at a loss with this.

Can you tell if 0.3.5 worked for you ? (Some earlier changes that might have affected locking were inadvertently removed in 0.3.5 and put back in develop). From your reference to #1468 I assume you are compiling with OpenMP, so
gcc -DNO_WARMUP -DUSE_OPENMP -DOS_LINUX -DSMP -DSMP_SERVER -E -I../.. memory.c >memory_processed.c
should reflect what the compiler sees - this should be very similar to what was in #1468

Actually #1468 was among the changes that were accidentally dropped in the creation of the "Frankenstein" version of memory.c that combined both TLS and non-TLS versions - this is supposed to have been rectified by #2004 three weeks ago but there may be an interaction with intervening changes from #1785 & #1814

gcc -I../.. -DOS_LINUX -DUSE_THREAD=1 -DUSE_OPENMP=1 -E -P memory.c will probably give the most concise preprocessing output.
And from this the crucial difference appears to be in the locking around the loop over memory[position].used (around line 1070 of the old memory.c, or line 2700 of the current version) - #1468 had the LOCK_COMMAND(&alloc_lock) around individual calls inside the do loop and conditional on !defined(USE_OPENMP) while the change from PR #1785 - being based on a version that inadvertently missed the changes from #1468 - not only moved the lock outside the loop in response to the observation of too many lock/unlock system calls but already lacked the conditionals. I seem to have missed this in #2004.

@martin-frbg I followed #2040 to https://raw.githubusercontent.com/xianyi/OpenBLAS/af480b02a4a45df377acf9be0d6078609bb345c2/driver/others/memory.c, copied this version of memory.c into a snapshot of the openblas development version from Friday and recompiled. This has restored the performance to where I would like it to be. Many thanks for your help and effort!

About a year ago or so, I presented a first comparison of openblas and blis/flame performance. Since then blis and libflame have caught up quite a bit and seem to have become threads-safe. In my standard code, I can use either openblas or blis/flame with OpenMP parallelisation. openblas tends to be around 1-4 % faster on small and moderately sized workloads. However, when pure DGEMM performance is required and the matrices become rather big, at the moment, openblas seems to be the clear winner. See below for a comparison with 1 and 16 threads. The results were obtained with openblas development and blis versions downloaded last Friday and after compilation using gcc9.0 on Ubuntu Linux 18.04 (4.15 kernel).
dgemm_amd_tr1950x

Thank you very much for the confirmation, merging that PR now.

Was this page helpful?
0 / 5 - 0 ratings