Zavallı işlev, hedef'in Linux üzerinde Performans | Netgez.com
SORU
1 NİSAN 2014, Salı


Zavallı işlev, hedef'in Linux üzerinde Performans

Son zamanlarda bazı yeni sunucular satın aldık ve zavallı işlev, hedef'in performans yaşıyoruz. Memcpy performansı 3x yavaş bizim dizüstü bilgisayarlar ile karşılaştırıldığında sunucularda.

Server Özellikleri

  • Kasa ve Anakart: SÃœPER MÄ°KRO 1027GR-SIRALANAN
  • Ä°ÅžLEMCÄ°: Intel E5-2680 @ Xeon 2.70 Ghz 2x
  • Bellek: 8x 16 GB DDR3 1600 MHz

Edit: biraz daha yüksek özellikleri olan bir sunucu üzerinde test ve yukarıdaki sunucusu olarak aynı sonuçları görüyorum

Server 2 Özellikleri

  • Kasa ve Anakart: SÃœPER MÄ°KRO 10227GR-TRFT
  • CPU: E5- @ 2.6 Ghz 2650 v2 . 2x, Intel tabanlı
  • Bellek: 16 GB DDR3 8x ben 1866mhz

Laptop Özellikleri

  • Kasa: Lenovo W530
  • Ä°ÅžLEMCÄ°: 1 x Intel Core i7 i7- @ 3720QM 2.6 Ghz
  • Bellek: 4x 4GB DDR3 1600 MHz

Ä°ÅŸletim Sistemi

$ 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

Derleyici (tüm sistemlerde)

$ gcc --version
gcc (GCC) 4.6.1

Ayrıca bir öneri dayalı gcc 4.8.2 ile test @stefan. Derleyiciler arasında performans farkı yok.

Test Kodu Test aşağıdaki kod üretim kodu olarak görüyorum sorun çoğaltmak için hazırlanmış bir testtir. Bu kriter basit ama bizim sorunumuz istismar tespit etmek mümkün olduğunu biliyorum. Kod aralarında iki 1GB tamponlar ve memcpys, işlev, hedef'in çağrı zamanlama oluşturur. Komut satırını kullanarak diğer arabellek boyutları belirtebilirsiniz: ./big_memcpy_test [SİZE_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;
}

Dosya Oluşturmak için CMake

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})

Test Sonuçları

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

Gördüğünüz gibi bizim sunucularda memcpys ve memsets bizim dizüstü üzerinde memcpys ve memsets yavaştır.

Tampon farklı boyutlarda

Benzer sonuçlar (sunucular laptop daha yavaş) ile 5 GB tüm 100MB dan tamponlar denedim

BenzeÅŸme NUMA

CPU ve bellek yakınlık numactl kullanarak ama sonuç aynı kaldı ayarı denedim yani insanlar NUMA ile performans sorunları hakkında okudum.

Sunucu Donanım NUMA

$ 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 Donanım NUMA

$ 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

NUMA Yakınlık ayarı

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

Herhangi bir yardım bu hadiseyi büyük beğeni topluyor.

Edit: GCC Seçenekleri

Yoruma dayalı farklı seçenekler GCC ile derleme denedim:

-Mart ve yerli-mtune set ile derleniyor

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

Sonuç: aynı performans (hiçbir gelişme)

-O2 ile derleme yerine -O3

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

Sonuç: aynı performans (hiçbir gelişme)

Edit: BOŞ sayfa (@SteveCox) önlemek için 0 yerine 0xF yazmaya Değişti memset

Değeri 0 (0xF bu durumda kullanılır) dışında memsetting ne zaman bir düzelme yok.

Edit: Cachebench sonuçları

Benim test programı çok basit olduğunu ekarte etmek için gerçek bir kıyaslama programı LLCacheBench (http://icl.cs.utk.edu/projects/llcbench/cachebench.html) indirdim

Her makinede kriter ayrı ayrı mimari sorunları önlemek için yaptım. Aşağıda benim sonuçlar.

laptop vs server memcpy performance

ÇOK büyük bir fark olduğunu daha büyük arabellek boyutları üzerinde performans. Son boyut (16777216) test 18849.29 yapılan sec, dizüstü bilgisayar ve 6710.40 sunucuda MB/. Bu performans hakkında 3x bir fark var. Ayrıca, sunucu performansı dropoff laptop daha çok dik olduğunu fark edebilirsiniz.

Edit: () hedef 2 kat daha HIZLI memcpy() sunucu üzerinde

Hedef kullanarak denedim bazı deneye dayalı() işlev, hedef'in yerine() testimi ve sunucu üzerinde 2x bir iyileşme var. () Hedef dizüstü bilgisayar daha yavaş çalışır memcpy() ama garip bir şekilde, hedef olarak aynı hızda çalışır yeterli() sunucu üzerinde. Bu soru yalvarır, işlev, hedef'in neden bu kadar yavaş?

Test için güncellenmiş Kod işlev, hedef'in birlikte hedef. Vardı kaydır hedef() içinde bir fonksiyon çünkü eğer ben sol satır GCC optimize edildi ve yapılan tam olarak aynı memcpy() (sanırım gcc optimize etmek için memcpy çünkü biliyordu yerleri yoktu üst üste).

Sonuçlar Güncellendi

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: Saf Ä°ÅŸlev, Hedef'in

Öneri @Salgar dayalı kendi saf memcpy fonksiyonu uygulanan ve test ettik.

Saf İşlev, Hedef'in Kaynağı

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  ;
  }
}

Saf işlev, hedef'in Sonuçları işlev, hedef'in Göre()

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

Edit: Derleme Çıktı

Basit bir işlev, hedef'in kaynağı

#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;
}

Derleme Çıktısı: Bu sunucu ve dizüstü hem de aynı. Yerden tasarruf ve ikisini yapıştırma ediyorum.

        .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, íi
        pushq   %rbx
        .cfi_def_cfa_offset 24
        .cfi_offset 3, -24
        subq    $8, %rsp
        .cfi_def_cfa_offset 32
        call    malloc
        movl    $1073741824, íi
        movq    %rax, %rbx
        call    malloc
        movl    $1073741824, íx
        movq    %rax, %rbp
        movl    $10, %esi
        movq    %rbx, %rdi
        call    memset
        movl    $1073741824, íx
        movl    $15, %esi
        movq    %rbp, %rdi
        call    memset
        movl    $1073741824, íx
        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    êx, êx
        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

!!!Ä°LERLEME! asmlib

Öneri @tbenson dayalı işlev, hedef'in asmlib sürümü ile çalıştırmayı denedim. Benim sonuçlar başlangıçta fakir ama SetMemcpyCacheLimit değiştirdikten sonra() döngü için saf! ile eşit hızda koşuyordum 1GB (arabellek boyutu)

Kötü haber hedef olan asmlib sürümünü abone olarak giriş sürümü, şimdi 300ms işareti çalışıyor (bu işlev, hedef'in abone olarak giriş sürümü ile aynı düzeyde) daha yavaş olmasıdır. Garip şey SetMemcpyCacheLimit zaman dizüstü() çok sayıda performans... ... canını yakıyor

Çizgiler SetCache ile işaretlenmiş aşağıda sonuçlar var 1073741824 için SetMemcpyCacheLimit ayarlayın. SetCache olmadan sonuçları SetMemcpyCacheLimit arama()

Sonuçlar asmlib fonksiyonları kullanarak:

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

Doğru cache sorunu yalın başlangıç, ama buna ne sebep olabilir?

CEVAP
2 NİSAN 2014, ÇARŞAMBA


[Bu bir yorum yapmak isterdim, ama bunu yapmak için yeterince ünü yok.]

Benzer bir sistemim var ve benzer sonuçlar, ama birkaç veri noktaları eklemek için:

  • EÄŸer memcpy saf (yani 33 ** dönüştürme) yönü ters ise, o zaman çok daha kötü olabilir ileriye doÄŸru daha performanslı (~637 benim için ms). DeÄŸiÅŸiklik oldu memcpy() glibc 2.12 bu açıkta çeÅŸitli böcek arama memcpy çakışan tamponlar (http://lwn.net/Articles/414467/) ve sanırım sorunu olmasından kaynaklandığını geçiÅŸ için bir sürümü memcpy geriye doÄŸru çalışır. Yani, geriye dönük karşı ileri kopyamemcpy()/memmove() farkı açıklayabilir.
  • Non-temporal maÄŸazaları kullanmamak daha iyi gibi görünüyor. Birçok memcpy() uygulamaları-zamansal olmayan depolar önbelleÄŸe olan) geçmek büyük tamponlar için (yani son seviyede daha büyük önbellek optimize edilmiÅŸ. Bu iÅŸlev, hedef'in Agner Fog'un sürüm (*48)* * * ve glibc sürümü olarak aynı hız yaklaşık buldum test ettim. Ancak, asmlib olan yukarıdaki eÅŸik ayarı izin veren bir iÅŸlev (SetMemcpyCacheLimit) non-temporal depolar kullanılır. 8GiB (veya 1 GiB tampon daha sadece daha büyük) sınır dışı geçici depolar önlemek için ayarı iki katına benim davam (176ms zaman aÅŸağı) performans. Tabii ki, o yıldız deÄŸil bu yüzden sadece ileri yönde saf performans tutuyor.
  • Bu sistemlerde BIOS dört farklı donanım prefetchers devre dışı (MLC Flama önceden getirici, Mekansal önceden getirici MLC, Flama önceden getirici DCU ve IP önceden getirici DCU)/etkin olmasını saÄŸlar. Her devre dışı, ama çok iyi muhafaza performans parite ve düşük performans yapmakta ayarları birkaç için çalıştım.
  • Çalışan ortalama güç sınırı (RAPL) devre dışı bırakma modu etkisi yoktur DRAM.
  • DiÄŸer Supermicro sistemleri Fedora 19 çalışan (abone olarak giriÅŸ 2.17) eriÅŸimi var. 19 ve " çok çekirdekli E5-2670 CPU Fedora X9DRG-HF Supermicro bir yönetim kurulu ile, yukarıdaki gibi benzer bir performans görüyorum. 9.6 bakın 19 Supermicro X10SLM-F tek soket Yönetim Kurulu Xeon E3-1275 v3 (Haswell) ve Fedora üzerinde çalışan GB memcpy (104ms)/s. Haswell sistem üzerindeki RAM DDR3-1600 (DiÄŸer sistemler gibi).

GÃœNCELLEÅžTÄ°RMELERÄ°

  • Maksimum Performans ve BIOS hiper iÅŸ parçacığı devre dışı CPU güç yönetimi kurdum. /proc/cpuinfo temel çekirdek 3 GHz saat hızına sahip sonra. Ancak garip bir ÅŸekilde bu hafıza performansı  civarında azaldı.
  • memtest86 4.10 raporları 9091 MB/s bellek bant geniÅŸliÄŸi. Bu okuma karşılık gelir, yazma veya kopyalama bulamadım.
  • STREAM benchmark raporlar 13422 MB/s kopyalama, ama onların sayısı bayt olarak hem okuma hem de yazılı, böylece karşılık gelen ~6.5 GB/s EÄŸer istediÄŸimiz karşılaÅŸtırmak için yukarıdaki sonuçları.

Bunu PaylaÅŸ:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VÄ°DEO

Rastgele Yazarlar

  • NewsyTech

    NewsyTech

    2 AÄžUSTOS 2010
  • The Verge

    The Verge

    8 AÄžUSTOS 2006
  • The White House

    The White Ho

    21 Ocak 2006

Ä°LGÄ°LÄ° SORU / CEVAPLAR