如何在Linux上为C ++应用程序分配“巨大”页面

时间:2015-09-18 13:08:14

标签: c++ linux performance optimization memory-management

我在Linux上有一个C ++应用程序,它对延迟非常敏感。我的内存使用量约为2GB,因此有4kb页面和64个TLB条目,我将遇到TLB未命中。

我在英特尔开发人员手册中读到2MB(或4MB?)“巨大”页面只能将TLB条目的数量减少一半,因此内存范围的增加抵消了TLB条目的减少,并且性能会更好

如何在C ++应用程序中使用“巨大”页面分配内存?我应该注意哪些权衡取舍?

我的Linux是Red Hat发行版。

3 个答案:

答案 0 :(得分:7)

我假设您只需要为使用C ++编写的特定应用程序提供大页面,否则您只需更改系统的页面大小。以下方法适用于以任何语言编写的应用程序。

  1. 为了在特定应用程序中使用大页面,您需要构建内核以支持大页面支持。您必须使用CONFIG_HUGETLBFS选项

  2. 构建内核
  3. 通过指定

    指定页面大小
    hugepagesz=<size>
    

    在启动命令行上

  4. 要了解如何设置启动参数:http://www.cyberciti.biz/tips/10-boot-time-parameters-you-should-know-about-the-linux-kernel.html

  5. 要设置大页面的数量,请使用

    # echo 20 > /proc/sys/vm/nr_hugepages
    
  6. 检查大页面(可用,总计......)

    # cat /proc/meminfo
    
  7. 当以上所有情况都很好时,现在您必须使用“如何将这些页面用于特定应用程序”:将hugetlbfs类型的文件系统挂载为

    # mount -t hugetlbfs -o uid=<value>,gid=<value>,mode=<value>,pagesize=<value>,size=<value>,min_size=<value>,nr_inodes=<value> none /mnt/huge
    

    将您的应用程序放在此挂载/mnt/huge繁荣上,现在您的应用程序将使用您设置的页面大小!

  8. 有关详细信息,请查看https://www.kernel.org/doc/Documentation/vm/hugetlbpage.txt

    大页面的优点/缺点:

    优点:由于TLB未命中减少,页面错误减少,页面大小减少以及翻译量减少而提高效率

    缺点:更多的内部碎片:内存丢失,交换延迟更多(HUGETLBFS页面不会刷出他们的映射是永久性的) 有关详情,请查看https://lwn.net/Articles/359158/

    修改 还有API可用于分配大页面plz检查也许它有帮助

    https://github.com/libhugetlbfs/libhugetlbfs/blob/master/HOWTO

    https://lwn.net/Articles/375096/

答案 1 :(得分:6)

"hugetlb" documentation from the kernel应该有帮助。

  

用户可以通过使用mmap系统调用或标准SYSV共享内存系统调用(shmget,shmat)来使用Linux内核中的大页面支持。

  

实施例

     

1)map_hugetlb:参见tools / testing / selftests / vm / map_hugetlb.c

     

2)hugepage-shm:参见tools / testing / selftests / vm / hugepage-shm.c

     

3)hugepage-mmap:参见tools / testing / selftests / vm / hugepage-mmap.c

     

4)libhugetlbfs(https://github.com/libhugetlbfs/libhugetlbfs)库     提供广泛的用户空间工具,以帮助实现巨大的页面和可用性,      环境设置和控制。

(这些路径指的是linux源代码树)。

所以它基本归结为:

  • mmapMAP_HUGETLB标志
  • 一起使用
  • 或者,映射挂载的hugetlb文件系统中的文件(如果存在)

答案 2 :(得分:3)

您还可以尝试使用过去几年中任何内核上可用的transparent huge page support(至少3.x和4.x范围内的任何内核以及各种2.6.x内核)。

主要的好处是你不需要任何特殊的&#34; hugetlbfs&#34;设置,它只是工作&#34;。缺点是它有保证:内核可能满足您的分配大页面如果满足某些条件且有些条件可用。与hugetlbfs不同,/sys/kernel/mm/transparent_hugepage/enabled在启动时保留固定数量的大页面,这些页面仅通过特定调用可用,透明的大页面可以从一般内存池中分割出大量页面。这需要连续的2MB物理内存块,由于物理内存碎片,这些块可能会随着时间的推移而变得罕见。

此外,还有各种内核可调参数会影响您是否获得大页面,其中最重要的是posix_memalign

最好的选择是使用madvise(MADV_HUGEPAGE)在2MB边界上分配块,然后在分配区域之前执行aligned_alloc 触摸它首次。它也适用于/sys/kernel/mm/transparent_hugepage/enabled等变体。根据我的经验,在always设置为malloc的系统上,这通常会导致巨大的页面。但是,我主要用于具有大量可用内存且运行时间不太长的系统。

如果您使用的是2GB内存,则可能会从大页面中获得显着的好处。如果您将所有内容分配为小块,例如通过 package com.example.hp.healthcareapp; import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.util.Patterns; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.Toast; import com.basgeekball.awesomevalidation.AwesomeValidation; import com.basgeekball.awesomevalidation.ValidationStyle; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; public class Registrationadmin extends AppCompatActivity { private EditText Nameadm, Emailadm, Passadm, Mobadm; private Button Registeradm; private AwesomeValidation awesomeValidation; String Name, Email, Password, Mob; Context ctx = this; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_registrationadmin); awesomeValidation = new AwesomeValidation(ValidationStyle.BASIC); Nameadm = (EditText) findViewById(R.id.etnameadm); Emailadm = (EditText) findViewById(R.id.etemailadm); Passadm = (EditText) findViewById(R.id.etpassadm); Mobadm = (EditText) findViewById(R.id.etmobadm); Registeradm = (Button) findViewById(R.id.btnregisteradm); awesomeValidation.addValidation(this, R.id.etnameadm, "^[A-Za-z\\s]{1,}[\\.]{0,1}[A-Za-z\\s]{0,}$", R.string.nameerror); awesomeValidation.addValidation(this, R.id.etemailadm, Patterns.EMAIL_ADDRESS, R.string.emailerror); // awesomeValidation.addValidation(this, R.id.etpasspat, "^[A-Za-z\\s]{1,}[\\.]{0,1}[A-Za-z\\s]{0,}$", R.string.passworderror); awesomeValidation.addValidation(this, R.id.etmobadm, "^[+]??[0-9]{10,13}$", R.string.mobileerror); //awesomeValidation.addValidation(this, R.id.etmobpat, RegexTemplate.TELEPHONE, R.string.mobileerror); //awesomeValidation.addValidation(this, R.id.etaddadm, "^[A-Za-z\\s]{1,}[\\.]{0,1}[A-Za-z\\s]{0,}$", R.string.adderror); String regexPassword = "(?=.*[a-z])(?=.*[A-Z])(?=.*[\\d])(?=.*[~`!@#\\$%\\^&\\*\\(\\)\\-_\\+=\\{\\}\\[\\]\\|\\;:\"<>,./\\?]).{8,}"; awesomeValidation.addValidation(this, R.id.etpassadm, regexPassword, R.string.passworderror); } public void btnregisteradm(View v) { if (awesomeValidation.validate()) { Name = Nameadm.getText().toString(); Password = Passadm.getText().toString(); Email = Emailadm.getText().toString(); Mob = Mobadm.getText().toString(); //Toast.makeText(ctx, "we are here", Toast.LENGTH_SHORT).show(); BackGround b = new BackGround(); //Toast.makeText(ctx, "we are here", Toast.LENGTH_SHORT).show(); b.execute(Name, Password, Email, Mob); //Toast.makeText(ctx, "we are here", Toast.LENGTH_SHORT).show(); } } static class BackGround extends AsyncTask<String, String, String> { @Override protected String doInBackground(String... params) { String admname = params[0]; String admpassword = params[1]; String admemail = params[2]; String admmob = params[3]; String data = ""; int tmp; try { URL url = new URL("http://10.14.83.2:8080/register.php"); String urlParams = "admname=" + admname + "&admemail=" + admemail + "&admpassword=" + admpassword + "&admmob=" + admmob; HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection(); httpURLConnection.setDoOutput(true); OutputStream os = httpURLConnection.getOutputStream(); os.write(urlParams.getBytes()); os.flush(); os.close(); Log.d("executes", "this is executed"); InputStream is = httpURLConnection.getInputStream(); Log.d("problem", "this line is not executed"); while ((tmp = is.read()) != -1) { data += (char) tmp; } Log.d("problem8", "why is this error"); is.close(); httpURLConnection.disconnect(); return data; } catch (MalformedURLException e1) { e1.printStackTrace(); Log.d("IOEX1", e1.getMessage()); return "Exception: " + e1.getMessage(); } catch (IOException e) { e.printStackTrace(); Log.d("IOEX3", e.getMessage()); return "Exception: " + e.getMessage(); } } @Override protected void onPostExecute(String s) { if(s.equals("")){ s="Data saved successfully."; } } }} ,很有可能透明的大页面无法启动,因此您也可以考虑以THP感知方式分配任何使用大量内存的内容(通常是单个对象类型) 。

我还写了a library以确定你是否从任何给定的分配中获得了大量的。这可能在生产应用程序中并不常用,但如果您尝试使用THP,它可能是一个有用的诊断,因为至少您可以确定是否得到它们。