Schlechte memcpy Performance unter Linux

Wir haben vor kurzem kaufte ein paar neue Server und erleben, schlechte memcpy Leistung. Der memcpy-Leistung ist 3x langsamer auf den Servern im Vergleich zu unseren laptops.

Server Specs

  • Gehäuse und Mobo: SUPER MIKRO-1027GR-TRF
  • CPU: 2x Intel Xeon E5-2680 @ 2.70 Ghz
  • Speicher: 8x 16GB DDR3 1600MHz

Edit: ich bin auch testen auf einem anderen server mit etwas höheren specs und sehen die gleichen Ergebnisse wie die oben genannten server

Server 2 Specs

  • Gehäuse und Mobo: SUPER MICRO 10227GR-TRFT
  • CPU: 2x Intel Xeon E5-2650 v2 @ 2.6 Ghz
  • Speicher: 8x 16GB DDR3 1.866 MHz

Laptop Specs

  • Chassis: Lenovo W530
  • CPU: 1x Intel Core i7 i7-3720QM @ 2.6 Ghz
  • Arbeitsspeicher: 4x 4GB DDR3 1600 MHz

Betriebssystem

$ cat /etc/redhat-release
Scientific Linux release 6.5 (Carbon) 
$ uname -a                      
Linux r113 2.6.32-431.1.2.el6.x86_64 #1 SMP Thu Dec 12 13:59:19 CST 2013 x86_64 x86_64 x86_64 GNU/Linux

Compiler (auf allen Systemen)

$ gcc --version
gcc (GCC) 4.6.1

Getestet auch mit gcc 4.8.2 basiert auf einem Vorschlag von @stefan. Es gab keinen performance-Unterschied zwischen Compiler.

Test-Code
Der test-code ist eine Dose testen, um das problem zu duplizieren, den ich sehe, in unserer Produktion code. Ich weiß, dass dieser benchmark ist simpel, aber es war in der Lage zu nutzen und erkennen unser problem. Der code erstellt zwei 1GB-Puffer und memcpys zwischen Ihnen, das timing der memcpy-Aufruf. Sie können angeben, Alternative buffer-Größen auf der Kommandozeile mittels: ./big_memcpy_test [SIZE_BYTES]

#include <chrono>
#include <cstring>
#include <iostream>
#include <cstdint>

class Timer
{
 public:
  Timer()
      : mStart(),
        mStop()
  {
    update();
  }

  void update()
  {
    mStart = std::chrono::high_resolution_clock::now();
    mStop  = mStart;
  }

  double elapsedMs()
  {
    mStop = std::chrono::high_resolution_clock::now();
    std::chrono::milliseconds elapsed_ms =
        std::chrono::duration_cast<std::chrono::milliseconds>(mStop - mStart);
    return elapsed_ms.count();
  }

 private:
  std::chrono::high_resolution_clock::time_point mStart;
  std::chrono::high_resolution_clock::time_point mStop;
};

std::string formatBytes(std::uint64_t bytes)
{
  static const int num_suffix = 5;
  static const char* suffix[num_suffix] = { "B", "KB", "MB", "GB", "TB" };
  double dbl_s_byte = bytes;
  int i = 0;
  for (; (int)(bytes / 1024.) > 0 && i < num_suffix;
       ++i, bytes /= 1024.)
  {
    dbl_s_byte = bytes / 1024.0;
  }

  const int buf_len = 64;
  char buf[buf_len];

  //use snprintf so there is no buffer overrun
  int res = snprintf(buf, buf_len,"%0.2f%s", dbl_s_byte, suffix[i]);

  //snprintf returns number of characters that would have been written if n had
  //      been sufficiently large, not counting the terminating null character.
  //      if an encoding error occurs, a negative number is returned.
  if (res >= 0)
  {
    return std::string(buf);
  }
  return std::string();
}

void doMemmove(void* pDest, const void* pSource, std::size_t sizeBytes)
{
  memmove(pDest, pSource, sizeBytes);
}

int main(int argc, char* argv[])
{
  std::uint64_t SIZE_BYTES = 1073741824; //1GB

  if (argc > 1)
  {
    SIZE_BYTES = std::stoull(argv[1]);
    std::cout << "Using buffer size from command line: " << formatBytes(SIZE_BYTES)
              << std::endl;
  }
  else
  {
    std::cout << "To specify a custom buffer size: big_memcpy_test [SIZE_BYTES] \n"
              << "Using built in buffer size: " << formatBytes(SIZE_BYTES)
              << std::endl;
  }


  //big array to use for testing
  char* p_big_array = NULL;

  /////////////
  //malloc 
  {
    Timer timer;

    p_big_array = (char*)malloc(SIZE_BYTES * sizeof(char));
    if (p_big_array == NULL)
    {
      std::cerr << "ERROR: malloc of " << SIZE_BYTES << " returned NULL!"
                << std::endl;
      return 1;
    }

    std::cout << "malloc for " << formatBytes(SIZE_BYTES) << " took "
              << timer.elapsedMs() << "ms"
              << std::endl;
  }

  /////////////
  //memset
  {
    Timer timer;

    //set all data in p_big_array to 0
    memset(p_big_array, 0xF, SIZE_BYTES * sizeof(char));

    double elapsed_ms = timer.elapsedMs();
    std::cout << "memset for " << formatBytes(SIZE_BYTES) << " took "
              << elapsed_ms << "ms "
              << "(" << formatBytes(SIZE_BYTES / (elapsed_ms / 1.0e3)) << " bytes/sec)"
              << std::endl;
  }

  /////////////
  //memcpy 
  {
    char* p_dest_array = (char*)malloc(SIZE_BYTES);
    if (p_dest_array == NULL)
    {
      std::cerr << "ERROR: malloc of " << SIZE_BYTES << " for memcpy test"
                << " returned NULL!"
                << std::endl;
      return 1;
    }
    memset(p_dest_array, 0xF, SIZE_BYTES * sizeof(char));

    //time only the memcpy FROM p_big_array TO p_dest_array
    Timer timer;

    memcpy(p_dest_array, p_big_array, SIZE_BYTES * sizeof(char));

    double elapsed_ms = timer.elapsedMs();
    std::cout << "memcpy for " << formatBytes(SIZE_BYTES) << " took "
              << elapsed_ms << "ms "
              << "(" << formatBytes(SIZE_BYTES / (elapsed_ms / 1.0e3)) << " bytes/sec)"
              << std::endl;

    //cleanup p_dest_array
    free(p_dest_array);
    p_dest_array = NULL;
  }

  /////////////
  //memmove
  {
    char* p_dest_array = (char*)malloc(SIZE_BYTES);
    if (p_dest_array == NULL)
    {
      std::cerr << "ERROR: malloc of " << SIZE_BYTES << " for memmove test"
                << " returned NULL!"
                << std::endl;
      return 1;
    }
    memset(p_dest_array, 0xF, SIZE_BYTES * sizeof(char));

    //time only the memmove FROM p_big_array TO p_dest_array
    Timer timer;

    //memmove(p_dest_array, p_big_array, SIZE_BYTES * sizeof(char));
    doMemmove(p_dest_array, p_big_array, SIZE_BYTES * sizeof(char));

    double elapsed_ms = timer.elapsedMs();
    std::cout << "memmove for " << formatBytes(SIZE_BYTES) << " took "
              << elapsed_ms << "ms "
              << "(" << formatBytes(SIZE_BYTES / (elapsed_ms / 1.0e3)) << " bytes/sec)"
              << std::endl;

    //cleanup p_dest_array
    free(p_dest_array);
    p_dest_array = NULL;
  }


  //cleanup
  free(p_big_array);
  p_big_array = NULL;

  return 0;
}

CMake-Datei Erstellen

project(big_memcpy_test)
cmake_minimum_required(VERSION 2.4.0)

include_directories(${CMAKE_CURRENT_SOURCE_DIR})

# create verbose makefiles that show each command line as it is issued
set( CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "Verbose" FORCE )
# release mode
set( CMAKE_BUILD_TYPE Release )
# grab in CXXFLAGS environment variable and append C++11 and -Wall options
set( CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++0x -Wall -march=native -mtune=native" )
message( INFO "CMAKE_CXX_FLAGS = ${CMAKE_CXX_FLAGS}" )

# sources to build
set(big_memcpy_test_SRCS
  main.cpp
)

# create an executable file named "big_memcpy_test" from
# the source files in the variable "big_memcpy_test_SRCS".
add_executable(big_memcpy_test ${big_memcpy_test_SRCS})

Testergebnisse

Buffer Size: 1GB | malloc (ms) | memset (ms) | memcpy (ms) | NUMA nodes (numactl --hardware)
---------------------------------------------------------------------------------------------
Laptop 1         | 0           | 127         | 113         | 1
Laptop 2         | 0           | 180         | 120         | 1
Server 1         | 0           | 306         | 301         | 2
Server 2         | 0           | 352         | 325         | 2

Wie Sie sehen können die memcpys und memsets auf unseren Servern ist sehr viel langsamer als die memcpys und memsets auf unseren laptops.

Unterschiedlichen puffergrößen

Habe ich versucht Puffer von 100 MB bis 5 GB, die alle ähnliche Ergebnisse (Server langsamer als laptop)

NUMA-Affinität

Ich lese über Menschen mit performance-Probleme mit NUMA so versuchte ich die Einstellung der CPU-und Speicher-Affinität mit numactl aber die Ergebnisse blieben die gleichen.

Server die NUMA-Hardware

$ numactl --hardware                                                            
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: 65501 MB                                                                        
node 0 free: 62608 MB                                                                        
node 1 cpus: 8 9 10 11 12 13 14 15 24 25 26 27 28 29 30 31                                   
node 1 size: 65536 MB                                                                        
node 1 free: 63837 MB                                                                        
node distances:                                                                              
node   0   1                                                                                 
  0:  10  21                                                                                 
  1:  21  10 

Laptop NUMA-Hardware

$ numactl --hardware
available: 1 nodes (0)
node 0 cpus: 0 1 2 3 4 5 6 7
node 0 size: 16018 MB
node 0 free: 6622 MB
node distances:
node   0 
  0:  10

Einstellung NUMA-Affinität

$ numactl --cpunodebind=0 --membind=0 ./big_memcpy_test

Jede Hilfe bei der Lösung dieses sehr zu schätzen.

Edit: GCC-Optionen

Basierend auf Kommentare, die ich versucht habe zu kompilieren mit unterschiedlichen GCC-Optionen:

Kompilieren mit-march und -mtune auf native gesetzt

g++ -std=c++0x -Wall -march=native -mtune=native -O3 -DNDEBUG -o big_memcpy_test main.cpp 

Ergebnis: Genau die gleiche Leistung (keine Verbesserung)

Kompilieren mit -O2 statt -O3

g++ -std=c++0x -Wall -march=native -mtune=native -O2 -DNDEBUG -o big_memcpy_test main.cpp

Ergebnis: Genau die gleiche Leistung (keine Verbesserung)

Edit: Geändert memset zu schreiben 0xF, anstatt von 0 zu vermeiden NULL-Seite (@SteveCox)

Keine Verbesserung, wenn memsetting mit einem anderen Wert als 0 (0xF in diesem Fall).

Edit: Cachebench Ergebnisse

Um auszuschließen, dass mein test-Programm ist zu simpel, ich heruntergeladen habe, ein echtes benchmarking-Programm LLCacheBench (http://icl.cs.utk.edu/projects/llcbench/cachebench.html)

Baute ich den benchmark auf jedem Rechner separat zu vermeiden, Architektur Themen. Hier sind meine Ergebnisse.

Schlechte memcpy Performance unter Linux

Beachten Sie die SEHR große Unterschied ist die Leistung auf den größeren buffer-Größen. Die Letzte Größe, die getestet (16777216) durchgeführt 18849.29 MB/s auf den laptop und 6710.40 auf dem server. Das ist etwa 3x Unterschied in der Leistung. Sie können auch feststellen, dass die Leistung Absetzung der server ist viel steiler als auf dem laptop.

Edit: memmove() ist 2x SCHNELLER als memcpy() auf dem server

Beruht auf einigen Experimenten habe ich versucht, mit memmove() anstelle von memcpy() in meinem test-Fall und habe ein 2x-Verbesserung auf dem server. Memmove() auf dem laptop läuft langsamer als memcpy (), aber merkwürdigerweise läuft mit der gleichen Geschwindigkeit wie die memmove() auf dem server. Dies wirft die Frage auf, warum ist memcpy so langsam?

Aktualisierten Code zu testen, memmove zusammen mit memcpy. Ich hatte zum wickeln des memmove() innerhalb einer Funktion, weil wenn ich es inline-GCC optimiert und durchgeführt, die genau das gleiche wie memcpy (), (ich nehme den gcc optimiert und es memcpy, weil Sie wusste, dass die Standorte nicht überschneiden).

Aktualisierte Ergebnisse

Buffer Size: 1GB | malloc (ms) | memset (ms) | memcpy (ms) | memmove() | NUMA nodes (numactl --hardware)
---------------------------------------------------------------------------------------------------------
Laptop 1         | 0           | 127         | 113         | 161       | 1
Laptop 2         | 0           | 180         | 120         | 160       | 1
Server 1         | 0           | 306         | 301         | 159       | 2
Server 2         | 0           | 352         | 325         | 159       | 2

Edit: Naive Memcpy

Basierend auf den Vorschlag von @Salgar implementierte ich meine eigene naive memcpy Funktion und es getestet.

Naiv Memcpy Quelle

void naiveMemcpy(void* pDest, const void* pSource, std::size_t sizeBytes)
{
  char* p_dest = (char*)pDest;
  const char* p_source = (const char*)pSource;
  for (std::size_t i = 0; i < sizeBytes; ++i)
  {
    *p_dest++ = *p_source++;
  }
}

Naiv Memcpy Ergebnisse im Vergleich zu memcpy()

Buffer Size: 1GB | memcpy (ms) | memmove(ms) | naiveMemcpy()
------------------------------------------------------------
Laptop 1         | 113         | 161         | 160
Server 1         | 301         | 159         | 159
Server 2         | 325         | 159         | 159

Edit: Assembly Output

Einfachen memcpy Quelle

#include <cstring>
#include <cstdlib>

int main(int argc, char* argv[])
{
  size_t SIZE_BYTES = 1073741824; //1GB

  char* p_big_array  = (char*)malloc(SIZE_BYTES * sizeof(char));
  char* p_dest_array = (char*)malloc(SIZE_BYTES * sizeof(char));

  memset(p_big_array,  0xA, SIZE_BYTES * sizeof(char));
  memset(p_dest_array, 0xF, SIZE_BYTES * sizeof(char));

  memcpy(p_dest_array, p_big_array, SIZE_BYTES * sizeof(char));

  free(p_dest_array);
  free(p_big_array);

  return 0;
}

Assembly Output: Dies ist genau das gleiche auf dem server und dem laptop. Ich bin platzsparend und nicht einfügen beide.

        .file   "main_memcpy.cpp"
        .section        .text.startup,"ax",@progbits
        .p2align 4,,15
        .globl  main
        .type   main, @function
main:
.LFB25:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movl    $1073741824, %edi
        pushq   %rbx
        .cfi_def_cfa_offset 24
        .cfi_offset 3, -24
        subq    $8, %rsp
        .cfi_def_cfa_offset 32
        call    malloc
        movl    $1073741824, %edi
        movq    %rax, %rbx
        call    malloc
        movl    $1073741824, %edx
        movq    %rax, %rbp
        movl    $10, %esi
        movq    %rbx, %rdi
        call    memset
        movl    $1073741824, %edx
        movl    $15, %esi
        movq    %rbp, %rdi
        call    memset
        movl    $1073741824, %edx
        movq    %rbx, %rsi
        movq    %rbp, %rdi
        call    memcpy
        movq    %rbp, %rdi
        call    free
        movq    %rbx, %rdi
        call    free
        addq    $8, %rsp
        .cfi_def_cfa_offset 24
        xorl    %eax, %eax
        popq    %rbx
        .cfi_def_cfa_offset 16
        popq    %rbp
        .cfi_def_cfa_offset 8
        ret
        .cfi_endproc
.LFE25:
        .size   main, .-main
        .ident  "GCC: (GNU) 4.6.1"
        .section        .note.GNU-stack,"",@progbits

FORTSCHRITT!!!! asmlib

Basierend auf den Vorschlag von @tbenson ich habe versucht mit mit dem asmlib version von memcpy. Meine Ergebnisse waren zunächst schlecht, doch nach dem Wechsel SetMemcpyCacheLimit() 1 GB (Größe der mein Puffer) lief ich bei der Geschwindigkeit auf Augenhöhe mit meiner naiven for-Schleife!

Schlechte Nachricht ist, dass die asmlib-version von memmove ist langsamer als die glibc-version, es läuft jetzt auf die 300ms Marke (auf Augenhöhe mit der glibc-version von memcpy). Komisch ist, dass auf dem laptop, wenn ich SetMemcpyCacheLimit (), um eine große Anzahl tut es weh, Leistung...

In den Ergebnissen unterhalb der markierten Zeilen mit SetCache haben SetMemcpyCacheLimit set zu 1073741824. Die Ergebnisse ohne SetCache nicht nennen SetMemcpyCacheLimit()

Ergebnisse mit Funktionen von asmlib:

Buffer Size: 1GB  | memcpy (ms) | memmove(ms) | naiveMemcpy()
------------------------------------------------------------
Laptop            | 136         | 132         | 161
Laptop SetCache   | 182         | 137         | 161
Server 1          | 305         | 302         | 164
Server 1 SetCache | 162         | 303         | 164
Server 2          | 300         | 299         | 166
Server 2 SetCache | 166         | 301         | 166

Ab zu lehnen in Richtung cache Problem, aber was würde das verursachen?

  • Sind Sie kompilieren der test auf dem server?
  • Ja, ich bin das kompilieren der test auf dem server. Beide Systeme sind mit der gleichen version von Scientific Linux. Beide haben die gleiche version von glibc (2.12) und gcc (4.6.1).
  • Können Sie überprüfen die code-Aufrufe in für memcpy? Meine erste Vermutung ist, dass vielleicht die server malloc ausgerichtet ist, anders als der laptop.
  • Versuchen Sie, kompilieren Sie Ihre main.cpp auf der Kommandozeile mit g++ -Wall -O2 -mtune=native. Erwägen Sie, GCC 4.8.2
  • Sie scheinen nicht zu kompilieren mit jedem Bogen-spezifische flags, die Sie sollten auf jeden Fall für das ein fairer test. that being said, ist dies definitiv ein Speicher-begrenzten Betrieb, und es sieht aus wie die Speicher-Spezifikationen sind nicht wirklich schneller auf den server, also es sollte nicht riesige Gewinne. sollte der server deutlich schneller als der laptops nur, wenn seine Arbeit von den cache-oder Register
  • oh auch nicht all diese Seiten auf null gesetzt, dass Sie etwas anderes. linux ist copy-on-write, und memset könnte optimiert werden, dass im Sinn. es sieht aus wie Sie könnte nur zeigen, um die triviale null-Seite, die macht dieser ganze test wertlos.
  • Ich bin damit einverstanden, ich sollte nicht sehen, riesige Gewinne von den Servern, aber ich bin besorgt, dass die Server langsamer sind als die laptops mit einem Faktor von 2-3x. Edit: ich werde versuchen, nicht memsetting die Seiten.
  • Nein, Sie müssen memset die Seiten, sondern stellen Sie Sie auf einen anderen Wert
  • hmm. es sieht aus wie Sie Messen einen Durchsatz von über 8 GB/s auf den laptops memset. Das ist ziemlich nahe an der theoretischen Grenze von 12,8. Ich würde erwarten, dass die memcpy, länger zu dauern, obwohl (nicht sicher, ob der Speicher-controller kann tun es alle auf Ihre eigene). Wenn es einen vollständigen lese-und Schreibzugriff eingebunden in die memcpy Umsetzung, dann ist das eine Bandbreite von 16 GB/s, die über die des Speichers auf die theoretischen Begrenzungen. Kann jemand bestätigen für mich, dass memcpying 1GB ist eigentlich eine 2-GB-operation?
  • Habe versucht mit memset ist mit einem Wert von 0xF anstelle von 0. Bekam die gleichen Ergebnisse.
  • Ist der laptop-Speicher gepuffert-ECC-DIMM ist wie die des Servers?
  • Laptop-Speicher us-unbuffered-DIMMS mit 1600 MHz. Ich Teste auf zwei Servern jetzt, etwa 6 Monate alt, mit buffered DIMMS mit 1600 MHz und eine weitere über 1 Monat alt mit buffered DIMMS zu 1.866 MHz. Beide Server haben die gleiche Leistung. ECC ist nicht aktiviert auf dem Server.
  • Wenn Sie reduzieren die server-Speicher-4 x 4G doe es die Geschwindigkeit verändern? Ich denke, dass die größeren Seite Tabellen haben eine Wirkung.
  • Auch ist HT aktiviert für den server oder den laptop?
  • ja, HT ist aktiviert auf beiden Rechnern. Ich derzeit nicht über physischen Zugriff auf den server da er sich in einem Labor über Land. Ich werde zu unserem anderen Standort, wo der server wohnt, in ein paar Tagen, werde ich versuchen, die Anpassung der RAM-Konfiguration.
  • 1) Beide sind 64 bit SO?; 2) Der server ist in einer VM?
  • Das klingt vielleicht albern, aber haben Sie zeitlich, wie lange Sie Ihr timing, dauert auf jedem Rechner? Es ist nicht gratis.
  • Eine andere Sache zu tun ist, schreiben Sie einfachen memcpy und memmove, und kompilieren Sie Sie down und vergleichen Sie die Baugruppe der beiden, um zu sehen, ob es irgendwelche signifikanten Unterschied in der Implementierung oder Optimierung auf den verschiedenen Maschinen.
  • Einmal kam ich über eine version von calloc das war nur eine for-Schleife Einstellung 4 Byte zu einem Zeitpunkt, es war tragisch langsam...
  • ja, alle Maschinen sind mit der exakt gleichen 64-bit-OS, genau die gleiche patch-Level. Nicht mit einer VM überall, alle blanken Metalle. System-Last auf alle Maschinen 0,0,0, ich bin die Steuerung dieser Maschinen und können überprüfen, es ist nichts Los in den hintergrund. Dies geschieht konsequent.
  • Nein, ich habe nicht getimed meine timings, aber ich wäre sehr überrascht, wenn es dauert 150 MS zum ausführen einer timer. Ich werde versuchen, eine vereinfachte memcpy und memmove Umsetzung.
  • Sorry, nicht Lesen es richtig, ja, die Zeit wird unbedeutend in dieser Größe/Zeit
  • Wow, das wird mehr und mehr interessant... ich kann nicht glauben, dass Sie Ihre byte-by-byte memcpy läuft schneller als libc ist!
  • Können Sie versuchen mit dem g++-4.8?
  • ich bin Gebäude mit 4.6.1 (ich update Frage) gibt es signifikante memcpy Verbesserungen zwischen 4.6.1 und 4.8.2?
  • Es ist möglich, ich weiß es nicht 😉 Der Punkt ist: es kann etwas sein, was der compiler tut (oder, wie der compiler konfiguriert wurde!), also wäre es toll zu testen, mit einem anderen compiler.
  • Zusammengestellt und lief mit 4.4.7 (Standard-compiler auf RHEL 6.5) und keine änderung. Ich bin Download 4.8.2, Gebäude, wird eine Weile dauern.
  • Geh und hol LIKWID und ausführen der performance-counter-tool wie dieses: likwid-perfctr -C S1:0 -g MEM ./big_memcpy_test. Auch laufen likwid-perfctr mit numactl --membind=0 seit likwid-perfctr nicht unterstützt NUMA-Speicher binden. Es zeigt Schätzungen der Speicher-Geschwindigkeiten basierend auf CPUs von Leistungsindikatoren und unterscheidet ebenfalls zwischen lokalen und remote-memory-access.
  • Ich würde gerne sehen, einige Assembler-code (für jede Plattform, da Sie nativ kompilieren). Vielleicht nutzt man AVX-Schleifen und die andere verwendet rep movs fließt?
  • Ja, bitte erstellen Sie ein main() ist nur, dass eine einzige große memcpy im auf und generieren Sie die Montage-Ausgang mit gcc/g++ -S + alle die gleichen Optimierungen, die Sie verwendet haben. Der zu langsam ist, muss etwas sehr seltsam, wenn Ihre benutzerdefinierte byte für byte memcpy ist so viel schneller. Es sollte viel langsamer.
  • Haben Sie überprüft, Ihre Speicher-alignment? Wenn die pipeline stalling auf dem server, dann würde man erwarten, dass memcpy Leistung zu beeinträchtigen.
  • Hinzugefügt assembly Ausgänge für laptop und server. Die Montage war genau der gleiche (diffed) ich habe die Ausgabe zu zeigen, was Sie beide aussahen. Nichts Interessantes passiert.
  • ich habe versucht, die Ausrichtung an 16byte Grenze mit dem link, den Sie zur Verfügung gestellt. Keine änderung in memcpy Leistung jedoch meine naive Implementierung lief ~600ms auf beiden Laptops und-server, 2x langsamer auf dem server als normale memcpy und 6x langsamer auf dem laptop (im Vergleich zu regulären memcpy).
  • Basierend auf den Vorschlag von @stefan ich habe versucht, mit einer neueren version von GCC, die ich wählte, 4.8.2 (die neueste version). Die performance-zahlen sind genau die gleichen wie mit 4.6.1 in der original-test.
  • Haben Sie sich alle Stromspar-Quatsch in der Server-BIOS aus?
  • Ich habe auch versucht, schieben Sie diese auf die Intel-community-forum (communities.intel.com/thread/50808). Ich habe ähnliche Leistung Merkwürdigkeiten mit einigen tuned out-of-place-matrix-transpose-Funktionen. Die optimierten Versionen verwenden cache-blocking, die sollen die Leistung verbessern (und eine transpose-ähnlich wie ein memcpy), aber auf der E5-Serie Xeons Sie tatsächlich langsamer. Das ist nicht der Fall für die Westmere-Xeons oder E3 Haswell-Xeons.
  • als ersten Schritt sollten Sie die Messung Speicher von Latenz und Bandbreite unter Verwendung von zum Beispiel Intel MLC oder TinyMemBench, oder lmbench oder was auch immer. Sicherlich die server-Teile haben schlechtere Latenz (wie ich es beschrieben in meiner Antwort), aber Sie wissen, wie viel helfen. Ferner mehrere dieser tools haben separate tests für die zeitliche und nicht-zeitliche speichert.

Schreibe einen Kommentar