Solidity: bool[](16) vs uint16 - ethereum

i'm in progress of Dapp developping.
My question is, in regards of gas economics,
bool[] a = new bool[](16);
and
uint16 a = 0;
Both has same gas cost?
Size of array will be constant.

bool[] a = new bool[](16);
Initialization of a storage fixed-size array creates pointers to N+1 storage slots, where N is the array length. Value stored in the first slot is the array length (in your case 16), the rest are values of its items.
In this case (not using structs), there is no optimization related to the datatype length. So even bool takes up the whole (32byte) slot.
uint16 a = 0;
Initialization of a storage integer creates a pointer to only 1 storage slot.
You can also save one write operation not using the redundant assign of 0 value, because 0 is the default value anyway. So you can do uint16 a; instead of uint16 a = 0;
So in short, it's ~17 times cheaper to initialize the uint (without assigning the zero value) compared to initialization of the array.

Related

In what slots are variables stored that are defined after an array in Solidity?

So I know how arrays are stored in storage. If I understand it correctly it first stores the number of items in an array in the first slot, and then in the next slots, it stores the hashed values.
My question is what if I define uint after the array and the array during deployment has only 2 values. So it should take up 3 slots. Then in the fourth slot is the uint I defined.
What if there is a function that will push something to the array? How is it stored?
Will it be stored in the next free slot? Or will it push the uint to the next slot and replace it with the new value?
I hope the question is clear if not I will try to rephrase it.
Also if there is some good resource where I can learn all about storage in solidity please share the link.
Thanks a lot!
Fixed-size array stores its values in sequential order, starting with the 0th index. There's no prepended slot that would show the total length. Any unset values use the default value of 0.
pragma solidity ^0.8;
contract MyContract {
address[3] addresses; // storage slots 0, 1, 2
uint256 number; // storage slot 3
constructor(address[2] memory _addresses, uint256 _number) {
addresses = _addresses;
number = _number;
}
}
Passing 2 addresses to the constructor, storage slot values in this case:
0: _addresses[0]
1: _addresses[1]
2: default value of zero (third address was not defined)
3: _number
Dynamic-size array stores its values in keys that are hash of the property storage slot (in example below that's 0, as that's the first storage property), and immediately following slots. In the property storage slot, it stores the array length.
pragma solidity ^0.8;
contract MyContract {
/*
* storage slots:
* p (in this case value 0, as this is the first storage property) = length of the array
* keccak256(p) = value of index 0
* keccak256(p) + 1 = value of index 1
* etc.
*/
address[] addresses;
// storage slot 1
uint256 number;
constructor(address[] memory _addresses, uint256 _number) {
addresses = _addresses;
number = _number;
}
}
Passing 2 addresses to the constructor, storage slot values in this case:
0: value 2 (length of the array)
1: _number
0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563 (hash of uint 0): _addresses[0]
0x290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e564 (hash of uint 0, plus 1): _addresses[1]
Docs: https://docs.soliditylang.org/en/v0.8.13/internals/layout_in_storage.html#mappings-and-dynamic-arrays
So to answer your questions:
What if there is a function that will push something to the array? How is it stored?
Will it be stored in the next free slot? Or will it push the uint to the next slot and replace it with the new value?
Fixed-size arrays cannot be resized. You can only rewrite its values, while the default value of each item is 0.
In case of dynamic-size arrays, it pushes the new value right after the last one. Since they are stored in slots which indexes are based on a hash, the probability of rewriting another value is practically 0 (i.e. that would mean a hash collision).
In both cases, it doesn't affect how other storage properties are stored.

How to use Critcl ByteArray?

I want to try out Critcl to enhance memory performance using a Z-order curve for a 2d-grid. What I need from Critcl is allocation, setter, getter and some size info. Reading about the Critcl ByteArray and examples does not make me confident on how to do it.
How do I create and return a ByteArray (i.e. Z-order curve)?
Any caveats I should know about when using ByteArray?
According to the documentation, you should be using the bytes type instead (when you get a pointer to a structure that has a len field with the number of bytes in it, and an s field that is the pointer to the actual read only block of bytes. (As a char * and not an unsigned char * for reasons I don't know. And why it isn't const is another mystery to me; there are cases where that's indeed true, but you need to look at the o field to figure that out.)
To return a byte array, you use the object (or object0) result type, and make the object with, for example, Tcl_NewByteArrayObj(), or Tcl_NewObj() and Tcl_SetByteArrayLength().
Here's an example (just the command definition) that does trivial byte reversing (since I don't understand Z-order curves at all):
critcl::cproc example {bytes dataPtr} object0 {
Tcl_Obj *result = Tcl_NewObj();
unsigned char *targetBytes = Tcl_SetByteArrayLength(result, dataPtr->len);
for (int i = 0, j = dataPtr->len - 1; j >= 0; i++, j--) {
targetBytes[i] = (unsigned byte) dataPtr->s[j];
}
return result;
}
Naturally, you'll want to read the Critcl usage guide when getting this to work, and if you're going to produce errors (by returning NULL), remember to set an error message in the interpreter. You can get access to that by using Tcl_Interp* interp as your first pseudo-argument to the command you create with critcl::cproc (it's documented, but easily missed).

Solidity and Web3 sha3() methods return something else

In my contract, I have a function that returns the sha3 hash of a certain set of values. While running some tests I found that the value returned from this function differs from the hash value generated by web3.utils.sha3() (with identical arguments).
Here is the code:
Solidity
function hashInfo() public onlyOwner view returns (bytes32) {
bytes32 hash = sha3(
'0x969A70A4fa9F69D2D655E4B743abb9cA297E5328',
'0x496AAFA2960f3Ff530716B5334c9aFf4612e3c27',
'jdiojd',
'oidjoidj',
'idjodj',
12345
)
return hash;
}
JS (web3)
async function testHash(instance){
const contractHash = await instance.methods.hashInfo().call({from: '0x969A70A4fa9F69D2D655E4B743abb9cA297E5328'});
const localHash = web3.utils.sha3(
'0x969A70A4fa9F69D2D655E4B743abb9cA297E5328',
'0x496AAFA2960f3Ff530716B5334c9aFf4612e3c27',
'jdiojd',
'oidjoidj',
'idjodj',
12345
)
console.log(contractHash);
console.log(localHash);
console.log('local == contract: ' + (contractHash == localHash));
}
The resulting console output is:
0xe65757c5a99964b72d217493c192c073b9a580ec4b477f40a6c1f4bc537be076
0x3c23cebfe35b4da6f6592d38876bdb93f548085baf9000d538a1beb31558fc6d
local == contract: false
Any ideas? Does this have something to do with passing multiple arguments to the functions? I have also tried to convert everything to a string and concatenate them into one single string, but also without success.
Thanks in advance!
UPDATE
I found out there also if a web3 method called web3.utils.soliditySha3(). This too did not work and gave the following result:
0xe65757c5a99964b72d217493c192c073b9a580ec4b477f40a6c1f4bc537be076
0x0cf65f7c81dab0a5d414539b0e2f3807526fd9c15e197eaa6c7706d27aa7a0f8
local == contract: false
I'm happy I came after your update as I was just gonna suggest solditySHA3. Now that you've got the right function your problem is most likely with Soldity packing it's parameters.
As you can see here, sha3 is an alias to keccak256 which tightly packs it's arguments. Following the link on that page takes you here which fully explains how it's handled. Basically just take the inputs to soliditySHA3 and pack the bits as if they were the sizes of the variables you used. So if you hashed two uint32s (32 bits each, 64 total) you need to take the 2 64 bit Javascript numbers and compress them into 1 Javascript number.
For cases where more than 64 bits are needed I believe you can pass sequential ints (sets of 64 bits) to soliditySHA3 or you could use a BigInt. Personally, I usually try to only hash 256 bit variables together to avoid having to manually pack my bits on the JS end, but we all know that space constraints are huge in Solidity. I hope I helped, and let me know if you have further questions.

Cuda/Thrust: remove_if doesn't change device_vector.size()?

I have a somewhat rather simple cuda question that seems like it should be a straight forward operation: removing elements from 1 array based on the value of a 2nd bool array. The steps I take are:
Create a device_vector of bools with the same size as the processed input array.
Call kernel which will set some of the elements from (1) to true
Call remove_if on input array with predicate using processed array from (2).
For each value in the bool array that is set to true, remove the corresponding element from the input array.
What I am seeing is that the input array isn't changed and I am not sure why ?
struct EntryWasDeleted
{
__device__ __host__
bool operator()(const bool ifDeleted)
{ return true; }
};
//This array has about 200-300 elements
//thrust::device_vector<SomeStruct> & arrayToDelete
thrust::device_vector<bool>* deletedEntries =
new thrust::device_vector<bool>(arrayToDelete.size(), false);
cuDeleteTestEntries<<<grid, block>>>( thrust::raw_pointer_cast(arrayToDelete.data()), countToDelete, heapAccess, thrust::raw_pointer_cast(deletedEntries->data()));
cudaDeviceSynchronize();
thrust::remove_if(arrayToDelete.begin(), arrayToDelete.end(), deletedEntries->begin(), EntryWasDeleted());
//I am expecting testEntries to have 0 elements
thrust::host_vector<SomeStruct> testEntries = arrayToDelete;
for( int i = 0; i<testEntries.size(); i++)
{ printf( "%d", testEntries[i].someValue); }
In this sample, I am always returning true in the predicate for testing. However, when I do: testEntries = deletedEntries and output the members. I can validate that deletedEntries is properly filled in with trues and falses.
My expectation would be that testEntries would have 0 elements. But it doesn't and I get an output as if remove_if didn't do anything. ie: the output is showing ALL elements from the input array. I am not sure why? Is there a specific way to remove elements from a device_vector?
So you need to capture the iterator that is being returned from remove_if
thrust::device_vector<SomeStruct>::iterator endIterator =
thrust::remove_if(arrayToDelete.begin(), arrayToDelete.end(),
deletedEntries->begin(), EntryWasDeleted());
Then when you copy data back to the host instead of using thrusts default assignment operator between host and device do this:
thrust::host_vector<SomeStruct> testEntries(arrayToDelete.begin(),endIterator);
As a side note working with arrays of primitives can often be much more efficient. Like can you store the index of your structs in an array instead and operate on those indexes?

How to concurrently write and read CUDA array with unique incrementing values?

I have a shared memory array initialized as follows
#define UNDEFINED 0xffffffff
#define DEFINED 0xfffffffe
__shared__ unsigned int array[100];
__shared__ count;
// We have enough threads: blockDim.x > 100
array[threadIdx.x] = UNDEFINED;
// Initialize count
if (threadIdx.x == 0)
count = 0;
The threads have random access to array. When a thread access array, if it is UNDEFINED, it must write a unique value, count, to that element, and then read that value. If the array element is DEFINED or already has a unique value, it must just read the unique value out. The tricky part is that array and count must both be updated by only 1 thread. Atomic functions only update 1 variable not 2. Here's the method that I finally came up with for 1 thread to update both variables while blocking the other threads until it is done.
value = atomicCAS(&array[randomIndex], UNDEFINED, DEFINED);
if (value == UNDEFINED) {
value = atomicAdd(&count, 1);
array[randomIndex] = value;
}
// For case that value == DEFINED_SOURCe, wait for memory
// writes, then store value
__threadfence_block();
value = array[randomSource];
There is some tricky concurrency going on here. I'm not sure that this will work for all cases. Are there better suggestions or comments?
According to your description, the only time an array element will be written to is if it contains the value UNDEFINED. We can leverage this.
A thread will first do an atomicCAS operation on the desired array element. The atomicCAS will be configured to check for the UNDEFINED value. If it is present, it will replace it with DEFINED. If it is not present, it will not replace it.
Based on the return result from atomicCAS, the thread will know if the array element contained UNDEFINED or not. If it did, then the return result from the atomicCAS will be UNDEFINED, and the thread will then go and retrieve the desired unique value from count, and use that to modify the DEFINED value to the desired unique value.
we can do this in one line of code:
// assume idx contains the desired offset into array
if (atomicCAS(array+idx, UNDEFINED, DEFINED) == UNDEFINED) array[idx]=atomicAdd(&count, 1);
A more complete code could be like this:
value = DEFINED;
while (value == DEFINED){
value = atomicCAS(&array[randomIndex], UNDEFINED, DEFINED);
if (value == UNDEFINED) {
value = atomicAdd(&count, 1);
array[randomIndex] = value;}
}
// value now contains the unique value,
// either that was already present in array[randomIndex]
// or the value that was just written there
For have an array of incrementing values, use prefx-sum also called scan algorithms, based on binary tree ower threads. First over local block(shared memory in the name) ? then global over blocks, then add each summ back to each block.
Also it may be efficient for each block to read not one but some values, what are equal of physically "warp size" like 16 int values for example ( i apologize, because i have done this things long time ago and don't know proper sizes and proper names for this things in CUDA).
Ahh, btw,the final values, in case of equal incrementing, could be retrieved as the function from local or global thread.id, so you do not need scan at all