小编ead*_*ead的帖子

在C++函数中"返回"的确切时刻

这似乎是一个愚蠢的问题,但是return xxx;在一个明确定义的函数中"执行" 的确切时刻?

请参阅以下示例以了解我的意思(现在直播):

#include <iostream>
#include <string>
#include <utility>

//changes the value of the underlying buffer
//when destructed
class Writer{
public:
    std::string &s;
    Writer(std::string &s_):s(s_){}
    ~Writer(){
        s+="B";
    }
};

std::string make_string_ok(){
    std::string res("A");
    Writer w(res);
    return res;
}


int main() {
    std::cout<<make_string_ok()<<std::endl;
} 
Run Code Online (Sandbox Code Playgroud)

我天真地期待发生的事情make_string_ok被称为:

  1. 构造函数res被调用(价值res就是"A")
  2. w调用构造函数
  3. return res被执行.应该返回res的当前值(通过复制当前值res),即"A".
  4. w被称为析构函数,值res变为"AB".
  5. 析构res函数被称为.

所以我希望"A"结果,但"AB" …

c++ object-lifetime language-lawyer copy-elision c++17

67
推荐指数
3
解决办法
4121
查看次数

列表初始化期间临时对象的生命周期

我一直认为,临时对象一直存在,直到完整表达结束.然而,这是a std::vector和数组的初始化之间的奇怪差异.

请考虑以下代码:

#include <iostream>
#include <vector>

struct ID{ 
  static int cnt;
  // the number of living object of class ID at the moment of creation:  
  int id;

  ID():id(++cnt){}

  ~ID(){
     cnt--;
  }
};

int ID::cnt=0;

int main(){

  int arr[]{ID().id, ID().id};
  std::vector<int> vec{ID().id, ID().id};

  std::cout<<" Array: "<<arr[0]<<", "<<arr[1]<<"\n";
  std::cout<<" Vector: "<<vec[0]<<", "<<vec[1]<<"\n";
}
Run Code Online (Sandbox Code Playgroud)

这个程序的输出有点意外(至少对我而言)意外:

 Array: 1, 1
 Vector: 1, 2
Run Code Online (Sandbox Code Playgroud)

这意味着,临时对象在整个初始化期间是活动的,std::vector但是在阵列的情况下它们被一个接一个地创建和销毁.我希望临时工可以活到完全表达int arr[]{ID().id, ID().id};完成之前.

该标准提到了一个关于临时对象的生命周期和数组初始化的例外(12.2).但是,我没有得到它的含义,也不知道为什么它适用于这种特殊情况:

有两种情况,临时表在与完整表达结束时不同的地方被摧毁.第一个上下文是调用默认构造函数来初始化数组的元素.如果构造函数具有一个或多个默认参数,则在构造下一个数组元素(如果有)之前,对默认参数中创建的每个临时的销毁进行排序.


不同编译器的结果概述(MSVS结果是NathanOliver的简称):

             Array    Vector
clang 3.8    1, 2      1, 2
g++ 6.1 …
Run Code Online (Sandbox Code Playgroud)

c++ g++ clang c++11

39
推荐指数
1
解决办法
1124
查看次数

Numpy:划分0.5的特别之处是什么?

这个答案 @Dunes的状态,即由于管道-ING有(几乎)没有浮点乘法和除法之间的差别.但是,从我对其他语言的看法来看,我认为这种划分会更慢.

我的小测试看起来如下:

A=np.random.rand(size)
command(A)
Run Code Online (Sandbox Code Playgroud)

对于不同的命令,size=1e8我在我的机器上得到以下时间:

Command:    Time[in sec]:
A/=0.5      2.88435101509
A/=0.51     5.22591209412
A*=2.0      1.1831600666
A*2.0       3.44263911247  //not in-place, more cache misses?
A+=A        1.2827270031
Run Code Online (Sandbox Code Playgroud)

最有趣的部分:除以0.5几乎是除以的两倍0.51.可以假设,这是由于一些智能优化,例如替换除法A+A.不过的时机A*2A+A太过离谱支持这一要求.

一般来说,使用浮点数除以值(1/2)^n的速度更快:

Size: 1e8
    Command:    Time[in sec]:
    A/=0.5      2.85750007629
    A/=0.25     2.91607499123
    A/=0.125    2.89376401901
    A/=2.0      2.84901714325
    A/=4.0      2.84493684769
    A/=3.0      5.00480890274
    A/=0.75     5.0354950428
    A/=0.51     5.05687212944
Run Code Online (Sandbox Code Playgroud)

如果我们看一下,它会变得更有趣size=1e4:

Command:    1e4*Time[in sec]:
A/=0.5      3.37723994255
A/=0.51     3.42854404449
A*=2.0      1.1587908268
A*2.0       1.19793796539
A+=A        1.11329007149
Run Code Online (Sandbox Code Playgroud)

现在,有一个由部门之间没有区别.5 …

python performance numpy

35
推荐指数
2
解决办法
813
查看次数

在L1缓存中缓存2KB数据时内存带宽崩溃的原因

在一个自学项目中,我借助以下代码测量内存的带宽(这里解释,整个代码在问题的最后跟随):

unsigned int doit(const std::vector<unsigned int> &mem){
   const size_t BLOCK_SIZE=16;
   size_t n = mem.size();
   unsigned int result=0;
   for(size_t i=0;i<n;i+=BLOCK_SIZE){           
             result+=mem[i];
   }
   return result;
}

//... initialize mem, result and so on
int NITER = 200; 
//... measure time of
   for(int i=0;i<NITER;i++)
       resul+=doit(mem)
Run Code Online (Sandbox Code Playgroud)

BLOCK_SIZE以这种方式选择,每个整数加法获取整个64字节高速缓存行.我的机器(Intel-Broadwell)每个整数附加需要大约0.35纳秒,所以上面的代码可以使带宽饱和到高达182GB/s(这个值只是一个上限,可能很大,很重要的是不同尺寸的带宽比率).代码用g++和编译-O3.

改变向量的大小,我可以观察到L1(*) - ,L2-,L3-高速缓存和RAM-内存的预期带宽:

在此输入图像描述

但是,我真的很难解释一下:L1高速缓存测量带宽的崩溃大小约为2 kB,这里的分辨率要高一些:

在此输入图像描述

我可以在我有权访问的所有机器上重现结果(具有Intel-Broadwell和Intel-Haswell处理器).

我的问题:内存大小大约2 KB的性能崩溃是什么原因?

(*)我希望我理解正确,对于L1缓存而言,不是64字节,而是每次添加只有4个字节被读取/传输(没有更快的缓存,其中必须填充缓存行),因此L1的绘制带宽是只有上限而不是badwidth本身.

编辑:当选择内部for循环中的步长时

  • 8(而不是16)崩溃发生1KB
  • 4(而不是16)崩溃发生0.5KB

即当内环由约31-35步/读时组成时.这意味着崩溃不是由于内存大小,而是由于内循环中的步数.

可以通过分支未命中来解释,如@ user10605163的好答案所示.


列出重现结果

bandwidth.cpp:

#include <vector>
#include <chrono>
#include <iostream>
#include <algorithm>


//returns minimal …
Run Code Online (Sandbox Code Playgroud)

c++ linux performance x86-64 intel

27
推荐指数
1
解决办法
507
查看次数

在C和C++中调用函数时,EAX寄存器初始化的差异

当编译为C程序或C++程序(对于Linux x86-64)时,小程序的程序集之间存在奇怪的差异.

有问题的代码:

int fun();
int main(){
    return fun();
}
Run Code Online (Sandbox Code Playgroud)

将其编译为C程序(带gcc -O2)产生:

main:
    xorl    %eax, %eax
    jmp fun
Run Code Online (Sandbox Code Playgroud)

但将其编译为C++ - 程序(带g++ -02)产生:

main:
    jmp _Z3funv
Run Code Online (Sandbox Code Playgroud)

我觉得很困惑,C版本用0(xorl %eax, %eax)初始化主函数的返回值.

C语言的哪个特性对这种必要性负责?

编辑:确实,因为int fun(void);没有初始化eax寄存器.

如果根本没有原型fun,即:

int main(){
    return fun();
}
Run Code Online (Sandbox Code Playgroud)

然后C编译器再次将eax寄存器归零.

c c++ assembly x86-64

23
推荐指数
2
解决办法
826
查看次数

pandas.unique()的好奇内存消耗

在分析我的算法的内存消耗时,我很惊讶有时对于较小的输入需要更多的内存.

这一切都归结为以下用法pandas.unique():

import numpy as np
import pandas as pd
import sys

N=int(sys.argv[1])

a=np.arange(N, dtype=np.int64)
b=pd.unique(a)
Run Code Online (Sandbox Code Playgroud)

N=6*10^7需要3.7GB的峰值内存,但与N=8*10^7"唯一" 3GB.

扫描不同的输入大小会产生以下图表:

在此输入图像描述

出于好奇和自我教育:如何反直觉的行为(即较小的输入大小更多的内存)左右N=5*10^7,N=1.3*10^7来解释?


以下是在Linux上生成内存消耗图的脚本:

pandas_unique_test.py:

import numpy as np
import pandas as pd
import sys

N=int(sys.argv[1])    
a=np.arange(N, dtype=np.int64)
b=pd.unique(a)
Run Code Online (Sandbox Code Playgroud)

show_memory.py:

import sys
import matplotlib.pyplot as plt   
ns=[]
mems=[]
for line in sys.stdin.readlines():
    n,mem = map(int, line.strip().split(" "))
    ns.append(n)
    mems.append(mem)
plt.plot(ns, mems, label='peak-memory')
plt.xlabel('n')
plt.ylabel('peak memory in …
Run Code Online (Sandbox Code Playgroud)

python algorithm performance pandas

14
推荐指数
2
解决办法
243
查看次数

是什么阻止了将函数参数用作隐藏指针?

我尝试了解System V AMD64 的含义-ABI的调用约定并查看以下示例:

struct Vec3{
    double x, y, z;
};

struct Vec3 do_something(void);

void use(struct Vec3 * out){
    *out = do_something();
}
Run Code Online (Sandbox Code Playgroud)

Vec3-variable是型存储器的,因此调用者(use)必须返回的变量分配空间并把它传递为隐藏指针到被叫方(即,do_something)。这是我们在生成的汇编器中看到的(在godbolt上,使用编译-O2):

use:
        pushq   %rbx
        movq    %rdi, %rbx           ;remember out
        subq    $32, %rsp            ;memory for returned object
        movq    %rsp, %rdi           ;hidden pointer to %rdi
        call    do_something
        movdqu  (%rsp), %xmm0        ;copy memory to out
        movq    16(%rsp), %rax
        movups  %xmm0, (%rbx)
        movq    %rax, 16(%rbx)
        addq    $32, …
Run Code Online (Sandbox Code Playgroud)

c optimization gcc x86-64 calling-convention

13
推荐指数
1
解决办法
235
查看次数

编译Python代码以使用Cython静态链接可执行文件

我有一个纯Python脚本,我想分发给具有未知Python配置的系统.因此,我想将Python代码编译为独立的可执行文件.

cython --embed ./foo.py没有问题就跑了foo.c.然后,我跑了

gcc $(python3-config --cflags) $(python3-config --ldflags) ./foo.c
Run Code Online (Sandbox Code Playgroud)

哪里python3-config --cflags

-I/usr/include/python3.5m -I/usr/include/python3.5m  -Wno-unused-result -Wsign-compare -g -fdebug-prefix-map=/build/python3.5-MLq5fN/python3.5-3.5.3=. -fstack-protector-strong -Wformat -Werror=format-security  -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes
Run Code Online (Sandbox Code Playgroud)

python3-config --ldflags给出

-L/usr/lib/python3.5/config-3.5m-x86_64-linux-gnu -L/usr/lib -lpython3.5m -lpthread -ldl  -lutil -lm  -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions
Run Code Online (Sandbox Code Playgroud)

这样我就可以获得一个没有问题的动态链接可执行文件.ldd a.out产量

 linux-vdso.so.1 (0x00007ffcd57fd000)
 libpython3.5m.so.1.0 => /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0 (0x00007fda76823000)
 libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fda76603000)
 libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fda763fb000)
 libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007fda761f3000)
 libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fda75eeb000)
 libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fda75b4b000)
 libexpat.so.1 => …
Run Code Online (Sandbox Code Playgroud)

python linker gcc cython

12
推荐指数
1
解决办法
7768
查看次数

为什么不对MEMORY类的类型执行尾调用优化?

我试图了解System V AMD64-ABI对于从函数按值返回的含义。

对于以下数据类型

struct Vec3{
    double x, y, z;
};
Run Code Online (Sandbox Code Playgroud)

该类型Vec3属于MEMORY类,因此ABI就“返回值”指定了以下内容:

  1. 如果类型具有MEMORY类,则调用方将为返回值提供空间,并以%rdi形式传递此存储的地址,就好像它是该函数的第一个参数一样。实际上,此地址成为“隐藏”的第一个参数。该存储不得与通过此参数以外的其他名称对被调用方可见的任何数据重叠。

    返回时,%rax将包含调用者在%rdi中传递的地址。

考虑到这一点,以下(傻)功能:

struct Vec3 create(void);

struct Vec3 use(){
    return create();
}
Run Code Online (Sandbox Code Playgroud)

可以编译为:

use_v2:
        jmp     create
Run Code Online (Sandbox Code Playgroud)

我认为,正如ABI所保证的,可以执行尾调用优化,这create会将%rdi传递的值放入%rax寄存器中。

但是,似乎没有一个编译器(gcc,clang和icc)正在执行此优化(此处为godbolt)。生成的汇编代码%rdi仅能将其值移动到%rax,因此保存在堆栈上,例如gcc:

use:
        pushq   %r12
        movq    %rdi, %r12
        call    create
        movq    %r12, %rax
        popq    %r12
        ret
Run Code Online (Sandbox Code Playgroud)

无论是对于这种最小的,愚蠢的功能,还是对于现实生活中更复杂的功能,都不​​会执行尾调用优化。这使我相信,我必须丢失某些东西,这是禁止的。


毋庸置疑,对于SSE类的类型(例如,仅2而不是3的两倍),将执行尾调用优化(至少由gcc和clang进行,仅靠戈德螺栓

use_v2:
        jmp     create
Run Code Online (Sandbox Code Playgroud)

结果是

use:
        jmp     create
Run Code Online (Sandbox Code Playgroud)

c optimization gcc x86-64 calling-convention

12
推荐指数
1
解决办法
123
查看次数

将多个子模块折叠为一个Cython扩展

这个setup.py:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

extensions = (
    Extension('myext', ['myext/__init__.py',
                        'myext/algorithms/__init__.py',
                        'myext/algorithms/dumb.py',
                        'myext/algorithms/combine.py'])
)
setup(
    name='myext',
    ext_modules=cythonize(extensions)
)
Run Code Online (Sandbox Code Playgroud)

没有预期的效果.我想要它生产单一的myext.so,它做的; 但是当我通过它调用它时

python -m myext.so
Run Code Online (Sandbox Code Playgroud)

我明白了:

ValueError: Attempted relative import in non-package
Run Code Online (Sandbox Code Playgroud)

由于myext试图参考的事实.algorithms.

知道如何让这个工作吗?

python distutils python-module cython

10
推荐指数
3
解决办法
3255
查看次数