这是我发布此程序的this question的后续内容:
#include <algorithm>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <vector>
#include <chrono>
class Stopwatch
{
public:
typedef std::chrono::high_resolution_clock Clock;
//! Constructor starts the stopwatch
Stopwatch() : mStart(Clock::now())
{
}
//! Returns elapsed number of seconds in decimal form.
double elapsed()
{
return 1.0 * (Clock::now() - mStart).count() / Clock::period::den;
}
Clock::time_point mStart;
};
struct test_cast
{
int operator()(const char * data) const
{
return *((int*)data);
}
};
struct test_memcpy
{
int operator()(const char * data) const
{
int result;
memcpy(&result, data, sizeof(result));
return result;
}
};
struct test_memmove
{
int operator()(const char * data) const
{
int result;
memmove(&result, data, sizeof(result));
return result;
}
};
struct test_std_copy
{
int operator()(const char * data) const
{
int result;
std::copy(data, data + sizeof(int), reinterpret_cast<char *>(&result));
return result;
}
};
enum
{
iterations = 2000,
container_size = 2000
};
//! Returns a list of integers in binary form.
std::vector<char> get_binary_data()
{
std::vector<char> bytes(sizeof(int) * container_size);
for (std::vector<int>::size_type i = 0; i != bytes.size(); i += sizeof(int))
{
memcpy(&bytes[i], &i, sizeof(i));
}
return bytes;
}
template<typename Function>
unsigned benchmark(const Function & function, unsigned & counter)
{
std::vector<char> binary_data = get_binary_data();
Stopwatch sw;
for (unsigned iter = 0; iter != iterations; ++iter)
{
for (unsigned i = 0; i != binary_data.size(); i += 4)
{
const char * c = reinterpret_cast<const char*>(&binary_data[i]);
counter += function(c);
}
}
return unsigned(0.5 + 1000.0 * sw.elapsed());
}
int main()
{
srand(time(0));
unsigned counter = 0;
std::cout << "cast: " << benchmark(test_cast(), counter) << " ms" << std::endl;
std::cout << "memcpy: " << benchmark(test_memcpy(), counter) << " ms" << std::endl;
std::cout << "memmove: " << benchmark(test_memmove(), counter) << " ms" << std::endl;
std::cout << "std::copy: " << benchmark(test_std_copy(), counter) << " ms" << std::endl;
std::cout << "(counter: " << counter << ")" << std::endl << std::endl;
}
我注意到,由于某些原因,std::copy
的性能比memcpy差得多。在我的Mac上使用gcc 4.7的输出看起来像这样。
g++ -o test -std=c++0x -O0 -Wall -Werror -Wextra -pedantic-errors main.cpp
cast: 41 ms
memcpy: 46 ms
memmove: 53 ms
std::copy: 211 ms
(counter: 3838457856)
g++ -o test -std=c++0x -O1 -Wall -Werror -Wextra -pedantic-errors main.cpp
cast: 8 ms
memcpy: 7 ms
memmove: 8 ms
std::copy: 19 ms
(counter: 3838457856)
g++ -o test -std=c++0x -O2 -Wall -Werror -Wextra -pedantic-errors main.cpp
cast: 3 ms
memcpy: 2 ms
memmove: 3 ms
std::copy: 27 ms
(counter: 3838457856)
g++ -o test -std=c++0x -O3 -Wall -Werror -Wextra -pedantic-errors main.cpp
cast: 2 ms
memcpy: 2 ms
memmove: 3 ms
std::copy: 16 ms
(counter: 3838457856)
您可以看到,即使-O3
,它也比memcpy慢5倍(!)。
结果与Linux相似。
有人知道为什么吗?
那不是我得到的结果:
> g++ -O3 XX.cpp > ./a.out cast: 5 ms memcpy: 4 ms std::copy: 3 ms (counter: 1264720400) Hardware: 2GHz Intel Core i7 Memory: 8G 1333 MHz DDR3 OS: Max OS X 10.7.5 Compiler: i686-apple-darwin11-llvm-g++-4.2 (GCC) 4.2.1
在Linux机器上,我得到不同的结果:
> g++ -std=c++0x -O3 XX.cpp > ./a.out cast: 3 ms memcpy: 4 ms std::copy: 21 ms (counter: 731359744) Hardware: Intel(R) Xeon(R) CPU E5-2670 0 @ 2.60GHz Memory: 61363780 kB OS: Linux ip-10-58-154-83 3.2.0-29-virtual #46-Ubuntu SMP Compiler: g++ (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3
[我同意@rici's comment关于开发更有意义的基准,因此我将您的测试重写为使用memcpy()
,memmove()
,std::copy()
和std::vector
赋值运算符对两个向量进行基准复制:
#include <algorithm>
#include <iostream>
#include <vector>
#include <chrono>
#include <random>
#include <cstring>
#include <cassert>
typedef std::vector<int> vector_type;
void test_memcpy(vector_type & destv, vector_type const & srcv)
{
vector_type::pointer const dest = destv.data();
vector_type::const_pointer const src = srcv.data();
std::memcpy(dest, src, srcv.size() * sizeof(vector_type::value_type));
}
void test_memmove(vector_type & destv, vector_type const & srcv)
{
vector_type::pointer const dest = destv.data();
vector_type::const_pointer const src = srcv.data();
std::memmove(dest, src, srcv.size() * sizeof(vector_type::value_type));
}
void test_std_copy(vector_type & dest, vector_type const & src)
{
std::copy(src.begin(), src.end(), dest.begin());
}
void test_assignment(vector_type & dest, vector_type const & src)
{
dest = src;
}
auto
benchmark(std::function<void(vector_type &, vector_type const &)> copy_func)
->decltype(std::chrono::milliseconds().count())
{
std::random_device rd;
std::mt19937 generator(rd());
std::uniform_int_distribution<vector_type::value_type> distribution;
static vector_type::size_type const num_elems = 2000;
vector_type dest(num_elems);
vector_type src(num_elems);
// Fill the source and destination vectors with random data.
for (vector_type::size_type i = 0; i < num_elems; ++i) {
src.push_back(distribution(generator));
dest.push_back(distribution(generator));
}
static int const iterations = 50000;
std::chrono::time_point<std::chrono::system_clock> start, end;
start = std::chrono::system_clock::now();
for (int i = 0; i != iterations; ++i)
copy_func(dest, src);
end = std::chrono::system_clock::now();
assert(src == dest);
return
std::chrono::duration_cast<std::chrono::milliseconds>(
end - start).count();
}
int main()
{
std::cout
<< "memcpy: " << benchmark(test_memcpy) << " ms" << std::endl
<< "memmove: " << benchmark(test_memmove) << " ms" << std::endl
<< "std::copy: " << benchmark(test_std_copy) << " ms" << std::endl
<< "assignment: " << benchmark(test_assignment) << " ms" << std::endl
<< std::endl;
}
[我出于娱乐目的对C ++ 11有点过分了。
std :: copy是根据memcpy实现的,但是显然gcc优化器无法弄清楚数据+ sizeof(int)-数据正是sizeof(int)。因此基准称为memcpy。
我通过用-S
调用gcc并快速浏览输出来获得所有这些;我很容易弄错了,但是我看到的似乎与您的测量结果一致。
memcpy
和std::copy
都有各自的用途,std::copy
应该(如下面的Cheers所指出的那样)速度与记忆一样慢,因为不能保证存储区域会重叠。这意味着您可以非常轻松地复制非连续区域(因为它支持迭代器)(请考虑稀疏分配的结构,例如链表等,甚至实现迭代器的自定义类/结构)。 memcpy
仅在连续的原因下工作,因此可以进行大量优化。test_memcpy
的汇编输出:$ g++ -Ofast a.cpp; ./a.out
cast: 24 ms
memcpy: 47 ms
memmove: 24 ms
std::copy: 24 ms
(counter: 1787289600)
$ g++ -O3 a.cpp; ./a.out
cast: 24 ms
memcpy: 24 ms
memmove: 24 ms
std::copy: 47 ms
(counter: 1787289600)
$ g++ --version
g++ (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008