我使用这样的代码来查找计算的数据值:
sub get_data {
$x =0 if($_[1] eq "A"); #get column number by name
$data{'A'}= [2.00000, 0.15000, -0.00143, 33.51030, 0.77, 1, 0, 12];
return $data{$_[0]}[$x];
}
数据在Perl文件中存储如下。我计划不超过100列。然后为了得到价值我打电话:
get_data(column, row);
现在我意识到在表格中查找数据的速度非常慢。我怎么能更快地做到这一点? SQL?
答案 0 :(得分:6)
查看你的github代码,你遇到的主要问题是你的 每次调用函数时都会初始化数组的大哈希值。
您当前的代码:
my @atom;
# {'name'}= radius, depth, solvation_parameter, volume, covalent_radius, hydrophobic, H_acceptor, MW
$atom{'C'}= [2.00000, 0.15000, -0.00143, 33.51030, 0.77, 1, 0, 12];
$atom{'A'}= [2.00000, 0.15000, -0.00052, 33.51030, 0.77, 0, 0, ''];
$atom{'N'}= [1.75000, 0.16000, -0.00162, 22.44930, 0.75, 0, 1, 14];
$atom{'O'}= [1.60000, 0.20000, -0.00251, 17.15730, 0.73, 0, 1, 16];
...
慢速上网本上的测试用例所花费的时间我正在输入:6m24.400s。
最重要的是将其移出功能,所以它就是这样 加载模块时只初始化一次。
这个简单的改变所花费的时间:1分20.714秒。
但是既然我正在提出建议,你可以更清晰地写出来:
my %atom = (
C => [ 2.00000, 0.15000, -0.00143, 33.51030, 0.77, 1, 0, 12 ],
A => [ 2.00000, 0.15000, -0.00052, 33.51030, 0.77, 0, 0, '' ],
...
);
请注意,%atom在两种情况下都是哈希值,因此您的代码不会对您执行任何操作 想象:它声明了一个词法范围的数组@atom,它是未使用的,然后继续填充一个不相关的全局变量%atom。 (你真的想要一个空的字符串用于MW的A吗?无论如何A是什么样的原子?)
其次,您的名称到数组索引映射也很慢。目前的代码:
#take correct value from data table
$x = 0 if($_[1] eq "radius");
$x = 1 if($_[1] eq "depth");
$x = 2 if($_[1] eq "solvation_parameter");
$x = 3 if($_[1] eq "volume");
$x = 4 if($_[1] eq "covalent_radius");
$x = 5 if($_[1] eq "hydrophobic");
$x = 6 if($_[1] eq "H_acceptor");
$x = 7 if($_[1] eq "MW");
作为哈希(再次,在函数外部初始化)这样做要好得多:
my %index = (
radius => 0,
depth => 1,
solvation_parameter => 2,
volume => 3,
covalent_radius => 4,
hydrophobic => 5,
H_acceptor => 6,
MW => 7
);
如果你愿意,你可能会变得时髦:
my %index = map { [qw[radius depth solvation_parameter volume
covalent_radius hydrophobic H_acceptor MW
]]->[$_] => $_ } 0..7;
无论哪种方式,函数内部的代码都是:
$x = $index{$_[1]};
现在时间:1分13.449秒。
另一种方法是将字段数定义为常量。 常量按惯例大写:
use constant RADIUS=>0, DEPTH=>1, ...;
然后函数中的代码是
$x = $_[1];
然后你需要使用常量而不是字符串来调用函数:
get_atom_parameter('C', RADIUS);
我没试过这个。
但稍微退一步看看你是如何使用这个功能的:
while($ligand_atom[$x]{'atom_type'}[0]) {
print STDERR $ligand_atom[$x]{'atom_type'}[0];
$y=0;
while($protein_atom[$y]) {
$d[$x][$y] = sqrt(distance_sqared($ligand_atom[$x],$protein_atom[$y]))
- get_atom_parameter::get_atom_parameter($ligand_atom[$x]{'atom_type'}[0], 'radius');
- get_atom_parameter::get_atom_parameter($protein_atom[$y]{'atom_type'}[0], 'radius');
$y++;
}
$x++;
print STDERR ".";
}
每次通过循环,您都会拨打get_atom_parameter
两次
检索半径。
但对于内环,一个原子始终是恒定的。所以提升电话
从内循环到get_atom_parameter
,你几乎减半了
通话次数:
while($ligand_atom[$x]{'atom_type'}[0]) {
print STDERR $ligand_atom[$x]{'atom_type'}[0];
$y=0;
my $lig_radius = get_atom_parameter::get_atom_parameter($ligand_atom[$x]{'atom_type'}[0], 'radius');
while($protein_atom[$y]) {
$d[$x][$y] = sqrt(distance_sqared($ligand_atom[$x],$protein_atom[$y]))
- $lig_radius
- get_atom_parameter::get_atom_parameter($protein_atom[$y]{'atom_type'}[0], 'radius');
$y++;
}
$x++;
print STDERR ".";
}
但还有更多。在你的测试案例中,配体有35个原子和
蛋白质4128原子。这意味着您的初始代码
4128 * 35 * 2 = 288960次拨打get_atom_parameter
,现在是。{
只有4128 * 35 + 35 = 144515次调用,只需制作一些数组即可
这个半径只有4128 + 35 = 4163个电话:
my $protein_size = $#protein_atom;
my $ligand_size;
{
my $x=0;
$x++ while($ligand_atom[$x]{'atom_type'}[0]);
$ligand_size = $x-1;
}
#print STDERR "protein_size = $protein_size, ligand_size = $ligand_size\n";
my @protein_radius;
for my $y (0..$protein_size) {
$protein_radius[$y] = get_atom_parameter::get_atom_parameter($protein_atom[$y]{'atom_type'}[0], 'radius');
}
my @lig_radius;
for my $x (0..$ligand_size) {
$lig_radius[$x] = get_atom_parameter::get_atom_parameter($ligand_atom[$x]{'atom_type'}[0], 'radius');
}
for my $x (0..$ligand_size) {
print STDERR $ligand_atom[$x]{'atom_type'}[0];
my $lig_radius = $lig_radius[$x];
for my $y (0..$protein_size) {
$d[$x][$y] = sqrt(distance_sqared($ligand_atom[$x],$protein_atom[$y]))
- $lig_radius
- $protein_radius[$y]
}
print STDERR ".";
}
最后,呼叫distance_sqared
[原文如此]:
#distance between atoms
sub distance_sqared {
my $dxs = ($_[0]{'x'}-$_[1]{'x'})**2;
my $dys = ($_[0]{'y'}-$_[1]{'y'})**2;
my $dzs = ($_[0]{'z'}-$_[1]{'z'})**2;
return $dxs+$dys+$dzs;
}
此功能可以有效地替换为使用的以下内容 乘法而不是**。
sub distance_sqared {
my $dxs = ($_[0]{'x'}-$_[1]{'x'});
my $dys = ($_[0]{'y'}-$_[1]{'y'});
my $dzs = ($_[0]{'z'}-$_[1]{'z'});
return $dxs*$dxs+$dys*$dys+$dzs*$dzs;
}
所有这些修改后的时间:0m53.639s。
有关**的更多信息:您声明的其他地方
use constant e_math => 2.71828;
并使用它:
$Gauss1 += e_math ** (-(($d[$x][$y]*2)**2));
内置函数exp()
为您计算(事实上,**通常是
实现为x**y = exp(log(x)*y)
,所以每次你这样做都是
执行不必要的对数,其结果略微减少
因为你的常数只能精确到6 d.p.而不是1。这种变化会改变
输出很轻微。同样,** 2应该用乘法代替。
无论如何,这个答案可能已经足够长了,并计算d[]
不再是瓶颈了。
总结:从循环和函数中提升常量值!计算 同样的事情一点也不好玩。
使用任何类型的数据库都不会对你的表现有所帮助
丝毫。可能对你有帮助的一件事是Inline::C
。 Perl是
并非真正为这种密集计算而构建,而Inline :: C
允许您轻松地将性能关键位移动到C中
在Perl中保留现有的I / O.
我愿意在部分C端口拍摄。多么稳定 是这个代码,你想要多快? :)
答案 1 :(得分:0)
将它放在数据库中会使维护,扩展,扩展等变得更容易....使用数据库也可以节省大量的RAM - 它只在RAM中存储和存储所需的结果而不是存储所有值。
关于速度,它取决于。使用文本文件需要很长时间才能将所有值读入RAM,但是一旦加载,检索值就会超快,比查询数据库更快。
所以这取决于你的程序是如何编写的以及它的用途。您是否读取了所有值ONCE然后运行1000个查询? TXT文件的方式可能更快。每次进行查询时都会读取所有值(以确保设置了最新的值) - 然后DB会更快。你每天1个查询吗?使用DB。等......