CJentzsch
10 years ago
50 changed files with 1387 additions and 256 deletions
@ -0,0 +1,181 @@ |
|||
<?xml version="1.0" encoding="UTF-8"?> |
|||
<ui version="4.0"> |
|||
<class>GasPricing</class> |
|||
<widget class="QDialog" name="GasPricing"> |
|||
<property name="geometry"> |
|||
<rect> |
|||
<x>0</x> |
|||
<y>0</y> |
|||
<width>416</width> |
|||
<height>286</height> |
|||
</rect> |
|||
</property> |
|||
<property name="windowTitle"> |
|||
<string>Gas Pricing</string> |
|||
</property> |
|||
<layout class="QGridLayout" name="gridLayout"> |
|||
<item row="3" column="0" colspan="2"> |
|||
<spacer name="verticalSpacer"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Vertical</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>398</width> |
|||
<height>45</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
<item row="4" column="0" colspan="2"> |
|||
<widget class="QLabel" name="label5_3"> |
|||
<property name="text"> |
|||
<string>&Bid: The minimum grice of gas that is accepting into a block which we mine.</string> |
|||
</property> |
|||
<property name="alignment"> |
|||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> |
|||
</property> |
|||
<property name="wordWrap"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="buddy"> |
|||
<cstring>bidValue</cstring> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="1" column="0"> |
|||
<widget class="QSpinBox" name="askValue"> |
|||
<property name="suffix"> |
|||
<string/> |
|||
</property> |
|||
<property name="maximum"> |
|||
<number>430000000</number> |
|||
</property> |
|||
<property name="value"> |
|||
<number>0</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="10" column="0" colspan="2"> |
|||
<layout class="QHBoxLayout" name="horizontalLayout"> |
|||
<item> |
|||
<spacer name="horizontalSpacer"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Horizontal</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>40</width> |
|||
<height>20</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
<item> |
|||
<widget class="QPushButton" name="close"> |
|||
<property name="text"> |
|||
<string>Close</string> |
|||
</property> |
|||
<property name="default"> |
|||
<bool>true</bool> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
</layout> |
|||
</item> |
|||
<item row="0" column="0" colspan="2"> |
|||
<widget class="QLabel" name="label5_2"> |
|||
<property name="text"> |
|||
<string>&Ask: The minimum grice of gas that is accepting into a block which we mine.</string> |
|||
</property> |
|||
<property name="alignment"> |
|||
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set> |
|||
</property> |
|||
<property name="wordWrap"> |
|||
<bool>true</bool> |
|||
</property> |
|||
<property name="buddy"> |
|||
<cstring>askValue</cstring> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="1" column="1"> |
|||
<widget class="QComboBox" name="askUnits"/> |
|||
</item> |
|||
<item row="5" column="0"> |
|||
<widget class="QSpinBox" name="bidValue"> |
|||
<property name="suffix"> |
|||
<string/> |
|||
</property> |
|||
<property name="maximum"> |
|||
<number>430000000</number> |
|||
</property> |
|||
<property name="value"> |
|||
<number>0</number> |
|||
</property> |
|||
</widget> |
|||
</item> |
|||
<item row="5" column="1"> |
|||
<widget class="QComboBox" name="bidUnits"/> |
|||
</item> |
|||
<item row="7" column="0" colspan="2"> |
|||
<spacer name="verticalSpacer_2"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Vertical</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>20</width> |
|||
<height>40</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
<item row="9" column="0" colspan="2"> |
|||
<spacer name="verticalSpacer_3"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Vertical</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>20</width> |
|||
<height>40</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
<item row="8" column="0" colspan="2"> |
|||
<spacer name="verticalSpacer_4"> |
|||
<property name="orientation"> |
|||
<enum>Qt::Vertical</enum> |
|||
</property> |
|||
<property name="sizeHint" stdset="0"> |
|||
<size> |
|||
<width>20</width> |
|||
<height>40</height> |
|||
</size> |
|||
</property> |
|||
</spacer> |
|||
</item> |
|||
</layout> |
|||
</widget> |
|||
<resources/> |
|||
<connections> |
|||
<connection> |
|||
<sender>close</sender> |
|||
<signal>clicked()</signal> |
|||
<receiver>GasPricing</receiver> |
|||
<slot>accept()</slot> |
|||
<hints> |
|||
<hint type="sourcelabel"> |
|||
<x>387</x> |
|||
<y>264</y> |
|||
</hint> |
|||
<hint type="destinationlabel"> |
|||
<x>240</x> |
|||
<y>262</y> |
|||
</hint> |
|||
</hints> |
|||
</connection> |
|||
</connections> |
|||
</ui> |
@ -1,4 +1,6 @@ |
|||
[ |
|||
{ "name": "eth_getWork", "params": [], "order": [], "returns": []}, |
|||
{ "name": "eth_submitWork", "params": ["", "", ""], "order": [], "returns": true} |
|||
{ "name": "eth_awaitNewWork", "params": [], "order": [], "returns": []}, |
|||
{ "name": "eth_progress", "params": [], "order": [], "returns": true} |
|||
] |
|||
|
@ -0,0 +1,225 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file ConstantOptimiser.cpp
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
#include "libevmasm/ConstantOptimiser.h" |
|||
#include <libevmasm/Assembly.h> |
|||
#include <libevmasm/GasMeter.h> |
|||
#include <libevmcore/Params.h> |
|||
using namespace std; |
|||
using namespace dev; |
|||
using namespace dev::eth; |
|||
|
|||
unsigned ConstantOptimisationMethod::optimiseConstants( |
|||
bool _isCreation, |
|||
size_t _runs, |
|||
Assembly& _assembly, |
|||
AssemblyItems& _items |
|||
) |
|||
{ |
|||
unsigned optimisations = 0; |
|||
map<AssemblyItem, size_t> pushes; |
|||
for (AssemblyItem const& item: _items) |
|||
if (item.type() == Push) |
|||
pushes[item]++; |
|||
for (auto it: pushes) |
|||
{ |
|||
AssemblyItem const& item = it.first; |
|||
if (item.data() < 0x100) |
|||
continue; |
|||
Params params; |
|||
params.multiplicity = it.second; |
|||
params.isCreation = _isCreation; |
|||
params.runs = _runs; |
|||
LiteralMethod lit(params, item.data()); |
|||
bigint literalGas = lit.gasNeeded(); |
|||
CodeCopyMethod copy(params, item.data()); |
|||
bigint copyGas = copy.gasNeeded(); |
|||
ComputeMethod compute(params, item.data()); |
|||
bigint computeGas = compute.gasNeeded(); |
|||
if (copyGas < literalGas && copyGas < computeGas) |
|||
{ |
|||
copy.execute(_assembly, _items); |
|||
optimisations++; |
|||
} |
|||
else if (computeGas < literalGas && computeGas < copyGas) |
|||
{ |
|||
compute.execute(_assembly, _items); |
|||
optimisations++; |
|||
} |
|||
} |
|||
return optimisations; |
|||
} |
|||
|
|||
bigint ConstantOptimisationMethod::simpleRunGas(AssemblyItems const& _items) |
|||
{ |
|||
bigint gas = 0; |
|||
for (AssemblyItem const& item: _items) |
|||
if (item.type() == Push) |
|||
gas += GasMeter::runGas(Instruction::PUSH1); |
|||
else if (item.type() == Operation) |
|||
gas += GasMeter::runGas(item.instruction()); |
|||
return gas; |
|||
} |
|||
|
|||
bigint ConstantOptimisationMethod::dataGas(bytes const& _data) const |
|||
{ |
|||
if (m_params.isCreation) |
|||
{ |
|||
bigint gas; |
|||
for (auto b: _data) |
|||
gas += b ? c_txDataNonZeroGas : c_txDataZeroGas; |
|||
return gas; |
|||
} |
|||
else |
|||
return c_createDataGas * dataSize(); |
|||
} |
|||
|
|||
size_t ConstantOptimisationMethod::bytesRequired(AssemblyItems const& _items) |
|||
{ |
|||
size_t size = 0; |
|||
for (AssemblyItem const& item: _items) |
|||
size += item.bytesRequired(3); // assume 3 byte addresses
|
|||
return size; |
|||
} |
|||
|
|||
void ConstantOptimisationMethod::replaceConstants( |
|||
AssemblyItems& _items, |
|||
AssemblyItems const& _replacement |
|||
) const |
|||
{ |
|||
assertThrow(_items.size() > 0, OptimizerException, ""); |
|||
for (size_t i = 0; i < _items.size(); ++i) |
|||
{ |
|||
if (_items.at(i) != AssemblyItem(m_value)) |
|||
continue; |
|||
_items[i] = _replacement[0]; |
|||
_items.insert(_items.begin() + i + 1, _replacement.begin() + 1, _replacement.end()); |
|||
i += _replacement.size() - 1; |
|||
} |
|||
} |
|||
|
|||
bigint LiteralMethod::gasNeeded() |
|||
{ |
|||
return combineGas( |
|||
simpleRunGas({Instruction::PUSH1}), |
|||
// PUSHX plus data
|
|||
(m_params.isCreation ? c_txDataNonZeroGas : c_createDataGas) + dataGas(), |
|||
0 |
|||
); |
|||
} |
|||
|
|||
CodeCopyMethod::CodeCopyMethod(Params const& _params, u256 const& _value): |
|||
ConstantOptimisationMethod(_params, _value) |
|||
{ |
|||
m_copyRoutine = AssemblyItems{ |
|||
u256(0), |
|||
Instruction::DUP1, |
|||
Instruction::MLOAD, // back up memory
|
|||
u256(32), |
|||
AssemblyItem(PushData, u256(1) << 16), // has to be replaced
|
|||
Instruction::DUP4, |
|||
Instruction::CODECOPY, |
|||
Instruction::DUP2, |
|||
Instruction::MLOAD, |
|||
Instruction::SWAP2, |
|||
Instruction::MSTORE |
|||
}; |
|||
} |
|||
|
|||
bigint CodeCopyMethod::gasNeeded() |
|||
{ |
|||
return combineGas( |
|||
// Run gas: we ignore memory increase costs
|
|||
simpleRunGas(m_copyRoutine) + c_copyGas, |
|||
// Data gas for copy routines: Some bytes are zero, but we ignore them.
|
|||
bytesRequired(m_copyRoutine) * (m_params.isCreation ? c_txDataNonZeroGas : c_createDataGas), |
|||
// Data gas for data itself
|
|||
dataGas(toBigEndian(m_value)) |
|||
); |
|||
} |
|||
|
|||
void CodeCopyMethod::execute(Assembly& _assembly, AssemblyItems& _items) |
|||
{ |
|||
bytes data = toBigEndian(m_value); |
|||
m_copyRoutine[4] = _assembly.newData(data); |
|||
replaceConstants(_items, m_copyRoutine); |
|||
} |
|||
|
|||
AssemblyItems ComputeMethod::findRepresentation(u256 const& _value) |
|||
{ |
|||
if (_value < 0x10000) |
|||
// Very small value, not worth computing
|
|||
return AssemblyItems{_value}; |
|||
else if (dev::bytesRequired(~_value) < dev::bytesRequired(_value)) |
|||
// Negated is shorter to represent
|
|||
return findRepresentation(~_value) + AssemblyItems{Instruction::NOT}; |
|||
else |
|||
{ |
|||
// Decompose value into a * 2**k + b where abs(b) << 2**k
|
|||
// Is not always better, try literal and decomposition method.
|
|||
AssemblyItems routine{u256(_value)}; |
|||
bigint bestGas = gasNeeded(routine); |
|||
for (unsigned bits = 255; bits > 8; --bits) |
|||
{ |
|||
unsigned gapDetector = unsigned(_value >> (bits - 8)) & 0x1ff; |
|||
if (gapDetector != 0xff && gapDetector != 0x100) |
|||
continue; |
|||
|
|||
u256 powerOfTwo = u256(1) << bits; |
|||
u256 upperPart = _value >> bits; |
|||
bigint lowerPart = _value & (powerOfTwo - 1); |
|||
if (abs(powerOfTwo - lowerPart) < lowerPart) |
|||
lowerPart = lowerPart - powerOfTwo; // make it negative
|
|||
if (abs(lowerPart) >= (powerOfTwo >> 8)) |
|||
continue; |
|||
|
|||
AssemblyItems newRoutine; |
|||
if (lowerPart != 0) |
|||
newRoutine += findRepresentation(u256(abs(lowerPart))); |
|||
newRoutine += AssemblyItems{u256(bits), u256(2), Instruction::EXP}; |
|||
if (upperPart != 1 && upperPart != 0) |
|||
newRoutine += findRepresentation(upperPart) + AssemblyItems{Instruction::MUL}; |
|||
if (lowerPart > 0) |
|||
newRoutine += AssemblyItems{Instruction::ADD}; |
|||
else if (lowerPart < 0) |
|||
newRoutine.push_back(Instruction::SUB); |
|||
|
|||
bigint newGas = gasNeeded(newRoutine); |
|||
if (newGas < bestGas) |
|||
{ |
|||
bestGas = move(newGas); |
|||
routine = move(newRoutine); |
|||
} |
|||
} |
|||
return routine; |
|||
} |
|||
} |
|||
|
|||
bigint ComputeMethod::gasNeeded(AssemblyItems const& _routine) |
|||
{ |
|||
size_t numExps = count(_routine.begin(), _routine.end(), Instruction::EXP); |
|||
return combineGas( |
|||
simpleRunGas(_routine) + numExps * (c_expGas + c_expByteGas), |
|||
// Data gas for routine: Some bytes are zero, but we ignore them.
|
|||
bytesRequired(_routine) * (m_params.isCreation ? c_txDataNonZeroGas : c_createDataGas), |
|||
0 |
|||
); |
|||
} |
@ -0,0 +1,147 @@ |
|||
/*
|
|||
This file is part of cpp-ethereum. |
|||
|
|||
cpp-ethereum is free software: you can redistribute it and/or modify |
|||
it under the terms of the GNU General Public License as published by |
|||
the Free Software Foundation, either version 3 of the License, or |
|||
(at your option) any later version. |
|||
|
|||
cpp-ethereum is distributed in the hope that it will be useful, |
|||
but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
GNU General Public License for more details. |
|||
|
|||
You should have received a copy of the GNU General Public License |
|||
along with cpp-ethereum. If not, see <http://www.gnu.org/licenses/>.
|
|||
*/ |
|||
/** @file ConstantOptimiser.cpp
|
|||
* @author Christian <c@ethdev.com> |
|||
* @date 2015 |
|||
*/ |
|||
|
|||
#pragma once |
|||
|
|||
#include <vector> |
|||
#include <libdevcore/CommonData.h> |
|||
#include <libdevcore/CommonIO.h> |
|||
|
|||
namespace dev |
|||
{ |
|||
namespace eth |
|||
{ |
|||
|
|||
class AssemblyItem; |
|||
using AssemblyItems = std::vector<AssemblyItem>; |
|||
class Assembly; |
|||
|
|||
/**
|
|||
* Abstract base class for one way to change how constants are represented in the code. |
|||
*/ |
|||
class ConstantOptimisationMethod |
|||
{ |
|||
public: |
|||
/// Tries to optimised how constants are represented in the source code and modifies
|
|||
/// @a _assembly and its @a _items.
|
|||
/// @returns zero if no optimisations could be performed.
|
|||
static unsigned optimiseConstants( |
|||
bool _isCreation, |
|||
size_t _runs, |
|||
Assembly& _assembly, |
|||
AssemblyItems& _items |
|||
); |
|||
|
|||
struct Params |
|||
{ |
|||
bool isCreation; ///< Whether this is called during contract creation or runtime.
|
|||
size_t runs; ///< Estimated number of calls per opcode oven the lifetime of the contract.
|
|||
size_t multiplicity; ///< Number of times the constant appears in the code.
|
|||
}; |
|||
|
|||
explicit ConstantOptimisationMethod(Params const& _params, u256 const& _value): |
|||
m_params(_params), m_value(_value) {} |
|||
virtual bigint gasNeeded() = 0; |
|||
virtual void execute(Assembly& _assembly, AssemblyItems& _items) = 0; |
|||
|
|||
protected: |
|||
size_t dataSize() const { return std::max<size_t>(1, dev::bytesRequired(m_value)); } |
|||
|
|||
/// @returns the run gas for the given items ignoring special gas costs
|
|||
static bigint simpleRunGas(AssemblyItems const& _items); |
|||
/// @returns the gas needed to store the given data literally
|
|||
bigint dataGas(bytes const& _data) const; |
|||
/// @returns the gas needed to store the value literally
|
|||
bigint dataGas() const { return dataGas(toCompactBigEndian(m_value, 1)); } |
|||
static size_t bytesRequired(AssemblyItems const& _items); |
|||
/// @returns the combined estimated gas usage taking @a m_params into account.
|
|||
bigint combineGas( |
|||
bigint const& _runGas, |
|||
bigint const& _repeatedDataGas, |
|||
bigint const& _uniqueDataGas |
|||
) |
|||
{ |
|||
// _runGas is not multiplied by _multiplicity because the runs are "per opcode"
|
|||
return m_params.runs * _runGas + m_params.multiplicity * _repeatedDataGas + _uniqueDataGas; |
|||
} |
|||
|
|||
/// Replaces the constant by the code given in @a _replacement.
|
|||
void replaceConstants(AssemblyItems& _items, AssemblyItems const& _replacement) const; |
|||
|
|||
Params m_params; |
|||
u256 const& m_value; |
|||
}; |
|||
|
|||
/**
|
|||
* Optimisation method that pushes the constant to the stack literally. This is the default method, |
|||
* i.e. executing it does not alter the Assembly. |
|||
*/ |
|||
class LiteralMethod: public ConstantOptimisationMethod |
|||
{ |
|||
public: |
|||
explicit LiteralMethod(Params const& _params, u256 const& _value): |
|||
ConstantOptimisationMethod(_params, _value) {} |
|||
virtual bigint gasNeeded() override; |
|||
virtual void execute(Assembly&, AssemblyItems&) override {} |
|||
}; |
|||
|
|||
/**
|
|||
* Method that stores the data in the .data section of the code and copies it to the stack. |
|||
*/ |
|||
class CodeCopyMethod: public ConstantOptimisationMethod |
|||
{ |
|||
public: |
|||
explicit CodeCopyMethod(Params const& _params, u256 const& _value); |
|||
virtual bigint gasNeeded() override; |
|||
virtual void execute(Assembly& _assembly, AssemblyItems& _items) override; |
|||
|
|||
protected: |
|||
AssemblyItems m_copyRoutine; |
|||
}; |
|||
|
|||
/**
|
|||
* Method that tries to compute the constant. |
|||
*/ |
|||
class ComputeMethod: public ConstantOptimisationMethod |
|||
{ |
|||
public: |
|||
explicit ComputeMethod(Params const& _params, u256 const& _value): |
|||
ConstantOptimisationMethod(_params, _value) |
|||
{ |
|||
m_routine = findRepresentation(m_value); |
|||
} |
|||
|
|||
virtual bigint gasNeeded() override { return gasNeeded(m_routine); } |
|||
virtual void execute(Assembly&, AssemblyItems& _items) override |
|||
{ |
|||
replaceConstants(_items, m_routine); |
|||
} |
|||
|
|||
protected: |
|||
/// Tries to recursively find a way to compute @a _value.
|
|||
AssemblyItems findRepresentation(u256 const& _value); |
|||
bigint gasNeeded(AssemblyItems const& _routine); |
|||
|
|||
AssemblyItems m_routine; |
|||
}; |
|||
|
|||
} |
|||
} |
Loading…
Reference in new issue