- r - 以节省内存的方式增长 data.frame
- ruby-on-rails - ruby/ruby on rails 内存泄漏检测
- android - 无法解析导入android.support.v7.app
- UNIX 域套接字与共享内存(映射文件)
编辑:得到一些反馈后,我创建了一个 new example这应该更具可重复性。
我一直在用 C++ 编写一个涉及大量链表迭代的项目。为了获得一个基准,我用 Go 重写了代码。令人惊讶的是,我发现 Go 实现的运行速度始终快约 10%,即使在将 -O 标志传递给 clang++ 之后也是如此。可能我只是在 C++ 中遗漏了一些明显的优化,但我一直在通过各种调整将我的头撞在墙上一段时间。
这是一个简化版本,在 C++ 和 Go 中有相同的实现,其中 Go 程序运行得更快。它所做的只是创建一个包含 3000 个节点的链表,然后计算迭代这个链表 1,000,000 次所需的时间(C++ 为 7.5 秒,Go 为 6.8 秒)。
C++:
#include <iostream>
#include <chrono>
using namespace std;
using ms = chrono::milliseconds;
struct Node {
Node *next;
double age;
};
// Global linked list of nodes
Node *nodes = nullptr;
void iterateAndPlace(double age) {
Node *node = nodes;
Node *prev = nullptr;
while (node != nullptr) {
// Just to make sure that age field is accessed
if (node->age > 99999) {
break;
}
prev = node;
node = node->next;
}
// Arbitrary action to make sure the compiler
// doesn't optimize away this function
prev->age = age;
}
int main() {
Node x = {};
std::cout << "Size of struct: " << sizeof(x) << "\n"; // 16 bytes
// Fill in global linked list with 3000 dummy nodes
for (int i=0; i<3000; i++) {
Node* newNode = new Node;
newNode->age = 0.0;
newNode->next = nodes;
nodes = newNode;
}
auto start = chrono::steady_clock::now();
for (int i=0; i<1000000; i++) {
iterateAndPlace(100.1);
}
auto end = chrono::steady_clock::now();
auto diff = end - start;
std::cout << "Elapsed time is : "<< chrono::duration_cast<ms>(diff).count()<<" ms "<<endl;
}
package main
import (
"time"
"fmt"
"unsafe"
)
type Node struct {
next *Node
age float64
}
var nodes *Node = nil
func iterateAndPlace(age float64) {
node := nodes
var prev *Node = nil
for node != nil {
if node.age > 99999 {
break
}
prev = node
node = node.next
}
prev.age = age
}
func main() {
x := Node{}
fmt.Printf("Size of struct: %d\n", unsafe.Sizeof(x)) // 16 bytes
for i := 0; i < 3000; i++ {
newNode := new(Node)
newNode.next = nodes
nodes = newNode
}
start := time.Now()
for i := 0; i < 1000000; i++ {
iterateAndPlace(100.1)
}
fmt.Printf("Time elapsed: %s\n", time.Since(start))
}
$ go run minimal.go
Size of struct: 16
Time elapsed: 6.865176895s
$ clang++ -std=c++11 -stdlib=libc++ minimal.cpp -O3; ./a.out
Size of struct: 16
Elapsed time is : 7524 ms
$ clang++ --version
Apple LLVM version 8.0.0 (clang-800.0.42.1)
Target: x86_64-apple-darwin15.6.0
Thread model: posix
// Fill in global linked list with 3000 contiguous dummy nodes
vector<Node> vec;
vec.reserve(3000);
for (int i=0; i<3000; i++) {
vec.push_back(Node());
}
nodes = &vec[0];
Node *curr = &vec[0];
for (int i=1; i<3000; i++) {
curr->next = &vec[i];
curr = curr->next;
curr->age = 0.0;
}
std::cout << &nodes << " " << &nodes->next << " " << &nodes->next->next << " " << &nodes->next->next->next << "\n";
0x1032de0e0 0x7fb934001000 0x7fb934001010 0x7fb934001020
最佳答案
前言:我不是C++专家或汇编专家。但我知道他们中的一点,也许足够危险。
所以我被激怒了,决定看一看为 Go 生成的汇编程序,然后根据 clang++ 的输出检查它。
高级摘要
稍后,我将在 x86-64 汇编器中查看两种语言的汇编器输出。本示例中代码的基本“关键部分”是一个非常紧密的循环。出于这个原因,它是在程序中花费的时间的最大贡献者。
为什么紧密循环很重要,因为现代 CPU 执行指令的速度通常比从内存中加载要引用的代码(例如比较)的相关值更快。为了实现它们所达到的极快的速度,CPU 执行了许多技巧,包括流水线、分支预测等。紧密循环通常是流水线的祸根,如果值之间存在依赖关系,那么实际上分支预测可能只会有一点帮助。
从根本上说,遍历循环有四个主要部分:
1. If `node` is null, exit the loop.
2. If `node.age` > 999999, exit the loop.
3a. set prev = node
3b. set node = node.next
3a, 1, 2, 3b
. Go 版本按顺序执行
3, 2, 1
. (它在第 2 段开始第一个循环以避免在空检查之前发生分配)
go build -gcflags -S main.go
为了获得这个程序集列表,我只是在看 iterateAndPlace。
"".iterateAndPlace STEXT nosplit size=56 args=0x8 locals=0x0
00000 (main.go:16) TEXT "".iterateAndPlace(SB), NOSPLIT, $0-8
00000 (main.go:16) FUNCDATA $0, gclocals·2a5305abe05176240e61b8620e19a815(SB)
00000 (main.go:16) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB)
00000 (main.go:17) MOVQ "".nodes(SB), AX
00007 (main.go:17) MOVL $0, CX
00009 (main.go:20) JMP 20
00011 (main.go:25) MOVQ (AX), DX
00014 (main.go:25) MOVQ AX, CX
00017 (main.go:25) MOVQ DX, AX
00020 (main.go:20) TESTQ AX, AX
00023 (main.go:20) JEQ 44
00025 (main.go:21) MOVSD 8(AX), X0
00030 (main.go:21) MOVSD $f64.40f869f000000000(SB), X1
00038 (main.go:21) UCOMISD X1, X0
00042 (main.go:21) JLS 11
00044 (main.go:21) MOVSD "".age+8(SP), X0
00050 (main.go:28) MOVSD X0, 8(CX)
00055 (main.go:29) RET
16 func iterateAndPlace(age float64) {
17 node := nodes
18 var prev *Node = nil
19
20 for node != nil {
21 if node.age > 99999 {
22 break
23 }
24 prev = node
25 node = node.next
26 }
27
28 prev.age = age
29 }
prev = node
.那是因为它意识到赋值可以被欺骗:在遍历中获取 node.next
它使用 CX 寄存器,其值为 prev
.这可能是一个很好的优化,SSA 编译器可以意识到这是多余的。 node = node.next
之后东西,在第一次迭代时被跳过。在这种情况下,您可以将其视为更像是 do..while 循环。总体较小,因为它只真正改变了第一次迭代。但也许这就够了? clang++ -S -mllvm --x86-asm-syntax=intel -O3 minimal.cpp
.
.quad 4681608292164698112 ## double 99999
# note I snipped some stuff here
__Z15iterateAndPlaced: ## @_Z15iterateAndPlaced
## BB#0:
push rbp
Lcfi0:
.cfi_def_cfa_offset 16
Lcfi1:
.cfi_offset rbp, -16
mov rbp, rsp
Lcfi2:
.cfi_def_cfa_register rbp
mov rcx, qword ptr [rip + _nodes]
xor eax, eax
movsd xmm1, qword ptr [rip + LCPI0_0] ## xmm1 = mem[0],zero
.p2align 4, 0x90
LBB0_2: ## =>This Inner Loop Header: Depth=1
mov rdx, rax
mov rax, rcx
movsd xmm2, qword ptr [rax + 8] ## xmm2 = mem[0],zero
ucomisd xmm2, xmm1
ja LBB0_3
## BB#1: ## in Loop: Header=BB0_2 Depth=1
mov rcx, qword ptr [rax]
test rcx, rcx
mov rdx, rax
jne LBB0_2
LBB0_3:
movsd qword ptr [rdx + 8], xmm0
pop rbp
ret
prev
进行了类似的优化。 .此外,C++ 似乎消除了每次比较时都加载 99999 的需要(Go 版本每次都在比较之前加载它)。
$ go version
go version go1.9.3 darwin/amd64
$ clang++ --version
Apple LLVM version 9.0.0 (clang-900.0.39.2)
Target: x86_64-apple-darwin17.4.0
关于c++ - 在 C++ 中迭代链表比在 Go 中慢,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50274433/
自己试试看: import pandas as pd s=pd.Series(xrange(5000000)) %timeit s.loc[[0]] # You need pandas 0.15.1
我最近开始使用 Delphi 中的 DataSnap 来生成 RESTful Web 服务。在遵循 Marco Cantu 本人和互联网上其他几个人的指导后,我成功地使整个“链条”正常工作。 但是有一
我一直在为操作系统类(class)编写以下代码,但结果有些奇怪。该代码创建x线程并同时运行它们,以便将两个平方矩阵相乘。每个线程将输入矩阵的Number_of_rows/Number_of_threa
我正在尝试确定何时使用 parallel包以加快运行某些分析所需的时间。我需要做的一件事是创建矩阵,比较具有不同行数的两个数据框中的变量。我在 StackOverflow 上问了一个关于有效方法的问题
我最近对我的代码进行了一些清理,并在此过程中更改了此内容(不完全是真实的代码): read = act readSTRef test1 term i var = do t v^!terms.
我正在计时查询和同一个查询的执行时间,分页。 foreach (var x in productSource.OrderBy(p => p.AdminDisplayName) .Wher
我正在开发一个项目 (WPF),我有一个 Datagrid 从数据库加载超过 5000 条记录,所以我使用 BackgroundWorker 来通知用户数据正在加载,但它太慢了,我需要等待将近 2分钟
我在查询中添加 ORDER BY 时遇到问题。没有 ORDER BY 查询大约需要 26ms,一旦我添加 ORDER BY,它大约需要 20s。 我尝试了几种不同的方法,但似乎可以减少时间。 尝试 F
我是 Android 开发新手,遇到了性能问题。当我的 GridView 有太多项目时,它会变得有点慢。有什么方法可以让它运行得更快一些吗? 这是我使用的代码: 适配器: public class C
这里的要点是: 1.设置query_cache_type = 0;重置查询缓存; 2.在 heidisql(或任何其他客户端 UI)中运行任何查询 --> 执行,例如 45 毫秒 3.使用以下代码运行
想象下表: CREATE TABLE drops( id BIGSERIAL PRIMARY KEY, loc VARCHAR(5) NOT NULL, tag INT NOT
我的表 test_table 中的示例数据: date symbol value created_time 2010-01-09 symbol1
首先,如果已经有人问过这个问题,我深表歉意,至少我找不到任何东西。 无论如何,我将每 5 分钟运行一次 cron 任务。该脚本加载 79 个外部页面,而每个页面包含大约 200 个我需要在数据库中检查
我有下面的 SQL 代码,它来自 MySQL 数据库。现在它给了我期望的结果,但是查询很慢,我想我应该在进一步之前加快这个查询的速度。 表agentstatusinformation有: PKEY(主
我需要获取一个对象在 Core Data 中数千个其他对象之间的排名。现在,这是我的代码: - (void)rankMethod { //Fetch all objects NSFet
我正在编写一个应用程序,我需要在其中读取用户的地址簿并显示他所有联系人的列表。我正在测试的 iPhone 有大约 100 个联系人,加载联系人确实需要很多时间。 ABAddressBookRef ad
我正在使用 javascript 将 160 行添加到包含 10 列的表格中。如果我这样做: var cellText = document.createTextNode(value); cell.a
我是 Swift 的新手,我已经设置了一个 tableView,它从 JSON 提要中提取数据并将其加载到表中。 表格加载正常,但是当表格中有超过 10 个单元格时,它会变得缓慢且有些滞后,特别是它到
我在 InitializeCulture 和 Page_PreInit 事件之间的 asp.net 页面中遇到性能问题。当我重写 DeterminePostBackMode() 时,我发现问题出在 b
我在 Hetzner 上有一个带有 256GB RAM 6 个 CPU(12 个线程) 的专用服务器,它位于德国。我有 CENTOS 7.5。 EA4。 我的问题是 SSL。每天大约 2 小时,我们在
我是一名优秀的程序员,十分优秀!