作者是Jonathan Boccara, Fluent C++作者,这个PPT就是卖书的
大纲
如果darknet要支持GPU和CUDNN的话,会有很多坑。
安装CUDA 两种方式,下载安装包和安装软件源
具体在https://developer.nvidia.com/cuda-downloads
我选的是网络安装deb
首先要下载deb文件,然后执行上面的步骤,cuda 就安装好了,默认在环境变量内。不用修改Makefile
如果是手动安装软件包,需要改动makefile
COMMON需要改正安装的路径 安装结束后,需要注意修改nvcc路径,不在环境变量中可能会识别不到,改下路径
安装CUDNN 这个没有办法,不能用命令行
https://developer.nvidia.com/rdp/cudnn-download
点第一个就可以(需要注册)
tar -zxvf cudnn-9.2-linux-x64-v7.1.tgz
cp cuda/include/cudnn.h /usr/local/cuda/include/
cp cuda/lib64/* /usr/local/cuda/lib64/
然后编译就可以了
笔记,不值得看。以前记在印象笔记的,搬迁出来做个记录
本来还在看cppcon2014,偶然翻到个和类型相关的演讲,以为是以为是PLT那种东西。学习过之后发现还是讨论的代数类型
sum type 和product type就不说了,主要是类型带来的重复和内耗,c++17带来了std::optional和 std::variant ,组织状态就可以用这俩,放弃原来的switch做法,转用match,缩小范围,全变成类型,更可控
中间有大量的篇幅推导product type的数量级,函数的数量级是指数级!
还讨论了个小插曲,在lua中1==true结果为false,因为不是同一个类型
作者的一个改造例子
原有方案
enum class ConnectionState{
DISCONNECTED,
CONNECTING,
CONNECTED,
CONNECTION_INTERRUPTED
};
struct Connection{
ConnectionState m_connectionState;
std::string m_serverAddress;
std::chrono::system_clock::time_point m_connectedTime;
std::chrono::millisecondes m_lastPingTime;
Timer m_reconnectTimer;
};
在看改造后
struct Connection{
std::string m_serverAddress;
struct Disconnected{};
struct Connecting{};
struct Connected{
ConnectionId m_id;
std::chrono::system_clock::time_point m_connectedTime;
std::chrono::millisecondes m_lastPingTime;
};
struct ConnectionInterrupted{
std::chrono::system_clock::time_point m_disconnectedTime;
Timer m_reconnectTimer;
};
std::variant<Disconnected,Connecting,Connected,ConnectionInterrupted> m_connection;
};
再举一个例子
class Friend{
std::string m_alias;
bool m_aliasPopulated;
};
两个字段到处同步,坑爹 -> std::optional<string> m_alias
这个讲的是scope_exit和栈回溯异常处理问题,用上了std::uncaught_exceptions
class UncaughtExceptionCounter {
int getUncaughtExceptionCount() noexcept;
int exceptionCount_ ;
public:
UncaughtExceptionCounter()
: exceptionCount_ (std::uncaught_exceptions()) {
}
bool newUncaughtException() noexcept {
return std::uncaught_exceptions() > exceptionCount _ ;
}
};
template <typename FunctionType, bool executeOnException>
class ScopeGuardForNewException {
FunctionType function_ ;
UncaughtExceptionCounter ec_ ;
public:
explicit ScopeGuardForNewException(const FunctionType& fn)
: function_ (fn) {}
explicit ScopeGuardForNewException(FunctionType&& fn)
: function_ (std::move(fn)) {}
~ScopeGuardForNewException() noexcept(executeOnException) {
if (executeOnException == ec_.isNewUncaughtException()) {
function_ ();
}
}
};
enum class ScopeGuardOnFail {};
template <typename FunctionType>
ScopeGuardForNewException<typename std::decay<FunctionType>::type, true>
operator+(detail::ScopeGuardOnFail, FunctionType&& fn) {
return ScopeGuardForNewException<
typename std::decay<FunctionType>::type, true>(
std::forward<FunctionType>(fn));
}
#define SCOPE_FAIL \
auto ANONYMOUS_VARIABLE(SCOPE_ FAIL_STATE) \
= ::detail::ScopeGuardOnFail() + [&]() noexcept
我的疑问,直接用scope_exit不行吗,貌似这个捕获了其他异常也会执行functor,不局限于本身的fail
需求不太一样
看到这里或许你有建议或者疑问,我的邮箱wanghenshui@qq.com 先谢指教。
这个讲的是scope_exit, std::range, make_range , operator <=> ,后面这两个已经进入c++标准了,前面这个有很多案例
先说scope_exit
作者写了个宏,显得scope_exit更好用一些,实际上没啥必要
他的实现类似这个
template <typename Callable> class scope_exit {
Callable ExitFunction;
bool Engaged = true; // False once moved-from or release()d.
public:
template <typename Fp>
explicit scope_exit(Fp &&F) : ExitFunction(std::forward<Fp>(F)) {}
scope_exit(scope_exit &&Rhs)
: ExitFunction(std::move(Rhs.ExitFunction)), Engaged(Rhs.Engaged) {
Rhs.release();
}
scope_exit(const scope_exit &) = delete;
scope_exit &operator=(scope_exit &&) = delete;
scope_exit &operator=(const scope_exit &) = delete;
void release() { Engaged = false; }
~scope_exit() {
if (Engaged)
ExitFunction();
}
};
几个疑问
第二个是make_iterable,对应标准库应该是std::range 本质上还是视图一类的东西。因为自定义类型,不想写一套iterator接口,给个view就行。没啥说的
第三个是operator<=> 实际上是解决comparator这种语义实现的问题
就比如rocksdb,bytewisecomparator
int r = memcmp(data_, b.data_, min_len);
if (r == 0) {
if (size_ < b.size_) r = -1;
else if (size_ > b.size_) r = +1;
}
return r;
内部肯定有三个分支,这是影响效率的,怎么搞?
再比如std::tuple 他是怎么比较的?
然后推导出一个适当的operator<=>来解决上述问题
传统写法
#define LOG(msg) \
if (s_bLoggingEnabled) \
std::cout<<__FILE__<<"("<<__LINE__<<")"<<msg<<std::endl;
汇编长这样
0x0000000000400c9d <+53>: mov %rax,%rdi
0x0000000000400ca0 <+56>: callq 0x400af0 <_ZNSaIcED1Ev@plt>
0x0000000000400ca5 <+61>: movzbl 0x201528(%rip),%eax # 0x6021d4 <s_bLoggingEnabled>
0x0000000000400cac <+68>: test %al,%al
0x0000000000400cae <+70>: je 0x400d2c <main()+196>
0x0000000000400cb0 <+72>: mov $0x400e5a,%esi
0x0000000000400cb5 <+77>: mov $0x6020c0,%edi
0x0000000000400cba <+82>: callq 0x400ad0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x0000000000400cbf <+87>: mov $0x400e65,%esi
0x0000000000400cc4 <+92>: mov %rax,%rdi
0x0000000000400cc7 <+95>: callq 0x400ad0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x0000000000400ccc <+100>: mov $0xe,%esi
0x0000000000400cd1 <+105>: mov %rax,%rdi
0x0000000000400cd4 <+108>: callq 0x400a70 <_ZNSolsEi@plt>
0x0000000000400cd9 <+113>: mov $0x400e67,%esi
0x0000000000400cde <+118>: mov %rax,%rdi
0x0000000000400ce1 <+121>: callq 0x400ad0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x0000000000400ce6 <+126>: mov $0x400e69,%esi
0x0000000000400ceb <+131>: mov %rax,%rdi
0x0000000000400cee <+134>: callq 0x400ad0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x0000000000400cf3 <+139>: mov %rax,%rdx
0x0000000000400cf6 <+142>: lea -0x40(%rbp),%rax
0x0000000000400cfa <+146>: mov %rax,%rsi
0x0000000000400cfd <+149>: mov %rdx,%rdi
0x0000000000400d00 <+152>: callq 0x400b50 <_ZStlsIcSt11char_traitsIcESaIcEERSt13basic_ostreamIT_T0_ES7_RKNSt7__cxx1112basic_stringIS4_S5_T1_EE@plt>
0x0000000000400d05 <+157>: mov $0x37,%esi
0x0000000000400d0a <+162>: mov %rax,%rdi
0x0000000000400d0d <+165>: callq 0x400a70 <_ZNSolsEi@plt>
0x0000000000400d12 <+170>: mov $0x400e6d,%esi
0x0000000000400d17 <+175>: mov %rax,%rdi
0x0000000000400d1a <+178>: callq 0x400ad0 <_ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc@plt>
0x0000000000400d1f <+183>: mov $0x400b10,%esi
0x0000000000400d24 <+188>: mov %rax,%rdi
作者说了log相关的指令问题,可能会阻止编译器优化,icache也不友好
针对此,要达到减少指令,保留速度,类型安全还方便,所以就用到表达式模板了,把工作放到编译期
怎么做?把所有打印的参数用表达式墨宝封装一下,typelist登场
拆成两部分,log 和logdata
logdata就是个表达式模板typelist,把所有的参数串起来
using namespace std;
#define LOG(msg) \
if (s_bLoggingEnabled) \
(log(__FILE__,__LINE__,LogData<None>()<<msg));
template <typename List>
struct LogData{
typedef List type;
List list;
};
struct None{};
template <typename Begin, typename Value>
LogData<std::pair<Begin&&, Value&&>> operator<<(LogData<Begin>&& begin,
Value&& v) noexcept {
return ;
}
template <typename Begin, size_t n >
LogData<std::pair<Begin&&, const char* >> operator<<(LogData<Begin>&& begin,
const char(&sz)[n]) noexcept{
return ;
}
template<typename TLogData>
void log(const char* file, int line, TLogData&& data)
noexcept {
std::cout<< file<<" "<<line<<": ";
LogRecursive(std::cout, std::forward<typename TLogData::type>(data.list));
std::cout<<std::endl;
}
template<typename TLogDataPair>
void LogRecursive(std::ostream& os, TLogDataPair&& data) noexcept{
LogRecursive(os,std::forward<typename TLogDataPair::first_type>(data.first));
os<<std::forward<typename TLogDataPair::second_type>(data.second);
}
inline void LogRecursive(std::ostream& os, None) noexcept{}
int main()
{
s_bLoggingEnabled = true;
string s{"blaa"};
LOG("sth"<<s<<55<<"!!");
}
0x0000000000400cab <+67>: movzbl 0x201522(%rip),%eax # 0x6021d4 <s_bLoggingEnabled>
0x0000000000400cb2 <+74>: test %al,%al
0x0000000000400cb4 <+76>: je 0x400d42 <main()+218>
0x0000000000400cba <+82>: movl $0x37,-0x44(%rbp)
0x0000000000400cc1 <+89>: lea -0x11(%rbp),%rax
0x0000000000400cc5 <+93>: mov $0x40130a,%esi
0x0000000000400cca <+98>: mov %rax,%rdi
0x0000000000400ccd <+101>: callq 0x400e24 <operator<< <None, 4ul>(LogData<None>&&, char const (&) [4ul])>
0x0000000000400cd2 <+106>: mov %rax,-0x30(%rbp)
0x0000000000400cd6 <+110>: mov %rdx,-0x28(%rbp)
0x0000000000400cda <+114>: lea -0xa0(%rbp),%rdx
0x0000000000400ce1 <+121>: lea -0x30(%rbp),%rax
0x0000000000400ce5 <+125>: mov %rdx,%rsi
0x0000000000400ce8 <+128>: mov %rax,%rdi
0x0000000000400ceb <+131>: callq 0x400ebd <operator<< <std::pair<None&&, char const*>, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>(LogData<std::pair<None&&, char const*> >&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&)>
0x0000000000400cf0 <+136>: mov %rax,-0x40(%rbp)
0x0000000000400cf4 <+140>: mov %rdx,-0x38(%rbp)
---Type <return> to continue, or q <return> to quit---
0x0000000000400cf8 <+144>: lea -0x44(%rbp),%rdx
0x0000000000400cfc <+148>: lea -0x40(%rbp),%rax
0x0000000000400d00 <+152>: mov %rdx,%rsi
0x0000000000400d03 <+155>: mov %rax,%rdi
0x0000000000400d06 <+158>: callq 0x400f6a <operator<< <std::pair<std::pair<None&&, char const*>&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>, int>(LogData<std::pair<std::pair<None&&, char const*>&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&> >&&, int&&)>
0x0000000000400d0b <+163>: mov %rax,-0x60(%rbp)
0x0000000000400d0f <+167>: mov %rdx,-0x58(%rbp)
0x0000000000400d13 <+171>: lea -0x60(%rbp),%rax
0x0000000000400d17 <+175>: mov $0x40130e,%esi
0x0000000000400d1c <+180>: mov %rax,%rdi
0x0000000000400d1f <+183>: callq 0x401002 <operator<< <std::pair<std::pair<std::pair<None&&, char const*>&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>&&, int&&>, 3ul>(LogData<std::pair<std::pair<std::pair<None&&, char const*>&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>&&, int&&> >&&, char const (&) [3ul])>
0x0000000000400d24 <+188>: mov %rax,-0x70(%rbp)
0x0000000000400d28 <+192>: mov %rdx,-0x68(%rbp)
0x0000000000400d2c <+196>: lea -0x70(%rbp),%rax
0x0000000000400d30 <+200>: mov %rax,%rdx
0x0000000000400d33 <+203>: mov $0x31,%esi
0x0000000000400d38 <+208>: mov $0x401311,%edi
0x0000000000400d3d <+213>: callq 0x401054 <log<LogData<std::pair<std::pair<std::pair<std::pair<None&&, char const*>&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>&&, int&&>&&, char const*> > >(char const*, int, LogData<std::pair<std::pair<std::pair<std::pair<None&&, char const*>&&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&>&&, int&&>&&, char const*> >&&)>
0x0000000000400d42 <+218>: lea -0xa0(%rbp),%rax
我个人测验,感觉并没有好到哪里去。。gcc开O3的话两个长得基本一致了。。就当学一下typelist转发技术好了
这个ppt讲的是如何把返回值用lambda包起来,让返回值auto,用作者的图来总结这篇内容
首先思考在Context下的调用
void callWithin(const std::function<void()>& fn){
ScopedContext context;
try{
fn();
} catch (SomeException& e){
// handle exception here
}
}
void printLine(const std::string& text){
std::cout<<text<<'\n';
}
callWithin([](){printLine("Hello, CppCon");});
回调小函数扔到lambda里 接口都操作lambda
也可以把这个变成模板, 这样 接口可以是任何类型的std::function, lambda
template <typename Callable>
void callWithin(const Callable& fn){
ScopedContext context;
fn();
}
进一步,如果想要回调函数的返回值,不需要要变动lambda接口
double sum(double a,double b){return a+b;}
double res = callWithin([](){return sum(3.14,2.71);})
可以在callWithin里改动lambda/function接口,但这降低了灵活性
double callWithin(const std::function<double()>&fn)...//如果返回值不是double怎么办?
解决办法,template auto-decltype
template <typename Callable>
auto callWithin(const Callable& fn) ->decltype(fn()){
decltype(fn()) result{};
auto wrapperFn =[&]()->void
{
result = fn();
}
callWithImpl(wrapperFn);
return result;
}
void callWithinImpl(const std::function<void()>& fn);
注意,这里用局部变量封一层result,弄一个局部的lambda,然后扔进callWithinImpl里,本质是加了一层,把原来的lambda放return的方案内嵌处理
传统方法,context肯定会有context manager,通过manager类接口来搞定, 接口也是固定的
class Contextmanager{
public:
virtual void callWithin(const std::function<void()>&fn) = 0;
};
然后整合上面的实现,大致这样
class Contextmanager{
public:
template <typename Fn>
void callWithin(const Fn& fn, std::false_type) -> decltype(fn())
{
decltype(fn()) result{};
callWithinImpl([&]{result=fn();});
return result;
}
private:
virtual void callWithinImpl(const std::function<void()>&fn) = 0;
};
double result = manager->CallWithin([]{return sum(3.14, 2/71);});
这个方案又有了新问题,原来的直接传void返回的functor不能工作了
特化吧
template <typename Fn>
auto callWithin(const Fn& fn) -> decltype(fn())
{
return _callWithin(fn, std::is_same<decltype(fn()),void>());
}
// true就直接调用,没有返回值
template <typename Fn>
void _callWithin(const Fn& fn, std::true_type) -> decltype(fn())
{
callWithinImpl([&]{fn();});
}
template <typename Fn>
void _callWithin(const Fn& fn, std::false_type) -> decltype(fn())
{
decltype(fn()) result{};
callWithinImpl([&]{result=fn();});
return result;
}
新的挑战,callWithin失败
所以还是需要内部callWithinImpl有个返回值,来设定result,需要std::optional 包装一下
template <typename Fn>
void callWithin(const Fn& fn) -> std::optional<decltype(fn())>
{
decltype(fn()) result{};
bool ok = callWithinImpl([&]{result=fn();});
if (ok)
return result;
else
return std::nullopt;
}
作者是Scott Meyers,这个内容就是他写的modern effective c++ 前几条
用一个图来概括
和auto相关的类型推导
一例
template <typenamet T>
void f(ParamType param);
f(expr);// 从expr推导T和ParamType
三种场景,ParamType可能是1)T& /T* 不是T&& 2)T&& 3)T
推导规则十分简单,
` 场景,ParamType=T&`
template <typename T>
void f(T& param);
int x=22;
const int cx = x;
const int& rx = x;
f(x); // T = int, param = int&
f(cx); // T = const int, param = const int&
f(rx); // T = const int, param = const int& 注意,此处的T ,expr是T& 直接忽略了&
` 场景,ParamType=const T&`
template <typename T>
void f(const T& praam);
int x=22;
const int cx = x;
const int& rx = x;
f(x); // T = int, param = const int& 注意,此处的T ,expr是const T& 直接忽略了
f(cx); // T = const int, param = const int&
f(rx); // T = const int, param = const int&
场景, ParamType=T*
template <typename T>
void f(T* praam);
int x=22;
const int *pcx = &x;
f(&x); // T = int, param = int *
f(pcx); // T = const int, param = const int *
场景,ParamType=T&&
如果涉及到万能引用,场景就会复杂,万能引用的退化方向比较多,值语义就是值语义,引用语义就是引用语义,万能引用表达的引用语义就比较杂,大部分场景和引用是一致的,除了
如果expr是左值,能推导出E,T能推导出E&,等价于expr是左值引用就会有引用折叠
听起来很复杂,就是所有左值的T都被推导出T&,右值推导出T&&,如果左值T是T&就折叠一个&,还是T&
template <typename T>
void f(T&& param);
//f(expr)
int x = 22;
const int cx = x;
const int& rx = x;
f(x); // T = int&, param = int&
f(cx); // T = const int&, param = const int&
f(rx); // T = const int&, param = const int& 折叠了一个&
f(22); // T = int, param = int&& 右值专属场景,完美转发
涉及到auto,如何推导?类似上面,引用会退化
int x = 22;
const int cx = x;
const int& rx = x;
auto& v1 = x; //auto = int;
auto& v2 = cx; //auto = const int
auto& v3 = rx; // auto = const int
const auto& v4 = x; // auto = int
const auto& v5 = cx; // auto =int
const auto& v6 = rx; // auto =int
auto v7 = x;// auto =int
auto v8 = cx;// auto = int
auto v9 = rx;// auto = int
auto&& v10=rx;// type=const int& 左值,引用折叠了
指针const auto推导
void someFunc(const int* const param1,
const int* param2,
int* param3)
{
auto p1=param1;// auto = const int* 忽略了最后那个
auto p2=param2;// const int*
auto& p2v2=param2;// const int* const&,这个没忽略,注意区别
auto p3=param3;// int*
}
会忽略指针const , 视*param为一体
如果expr是const 或者volatile,直接忽略cv限定
还有针对函数指针和数组指针退化行为的边角场景,一律退化成指针
{}表达式推导成初始化列表,注意函数参数会推导失败
还有一大堆细节不列了,有点语言律师的赶脚
lambda捕获类型推导
默认lambda是const的捕获内部参数不能直接改,需要显式mutable
observing deduced types
作者的一个小技巧,不实现类,来通过编译器的推导和编译报错观察推导类型
template <typename T>
class TD;
template <typename T>
void f(T& param){
TD<T> tType;
TD<decltype(param)> paramType;
}
作者也介绍了type_info和boost::type_index 不赘述
decltype推导
函数返回值类型推导