Cycle Costs, HashMap, Buffers

I’ve been thinking through the use of hashmaps to store some large data chunks and I wanted to think through how the data is stored and how the cycle usage is calculated.

Say I have a HashMap<Nat, Buffer>. When I .get a buffer out of the map, what is happening? Since Buffers are an object like entity, am I just getting a Ref to an existing object? If I manipulate the Buffer do I have to .put it back? I know if it were an array, I’d have to put back a different array that I’d create with the mutations I want. But I’m not sure about buffer.

If the .get has to create a new copy of the buffer in the HashMap, how expensive is this? Is it just a memcopy? And is that expensive?

If I have to .put the changed buffer back into the hash map, am I making an even newer copy? And is that expensive?

If I’m having to get and put back a bunch of copies with HashMap, would it make more sense to just have an Initialized array of a bunch of Buffers that I can use without having to reinstantiate a bunch of buffers in and out of HashMaps?

Perhaps .get does get a ref to the existing object in orthogonal memory and I’m all good and no .put is necessary. I guess an experiment is warranted, but I thought I’d ask first in case anyone had a clear answer.

2 Likes

I guess I should update since I seem to have solved my own problem.

If you .get() a buffer out of a HashMap and update the buffer, you do not have to .put() the buffer back. In a nice OO fashion, the buffer seemed to be reference ByRef and all updates to the buffer take place with the promised orthogonal persistence one would hope for.

I hope this clarifies things for someone else as it was confusing for me at first.

9 Likes

Hi @skilesare

I’m having doubts the way I update the existing data on buffer. What I do is take the index and place the new data in it.

I believe using index is not the best way to update existing data on a buffer but at the moment it is the possible way to do it.

Below is my code.

import Buffer “mo:base/Buffer”;
import Principal “mo:base/Principal”;
import Map “mo:hashmap/Map”;

let { phash } = Map;

type Note = {
	id : Text;
	content : Text;
};

var notesByUser : Map.Map<Principal, Buffer.Buffer<Note>> = Map.new<Principal, Buffer.Buffer<Note>>(phash);

public shared ({ caller }) func updateNote (index : Nat, note : Note) : async () {
	let userNotes : Buffer.Buffer<Note> = switch (Map.get(notesByUser, phash, caller)) {
		case null Buffer.Buffer<Note>(0);
		case (?v) v;
	};

	let newNote : Note = {
		id = note.id;
		content = note.content;
	};

	userNotes.put(index, newNote);
};

Please let me know your thoughts.

Thank you.

1 Like

I don’t see any issues with what you are doing…are you having specific issues?

2 Likes

@skilesare my code works well, but I doubt about it if it is the correct way to update an existing data on a Buffer, for this example what I’m referring to is using the index to update the data, I think it is risky to use index?

Is there a way I can do it like this, find this “note id” in a Buffer and if found update it with new note data?

Or my code is good already and I’m just making it complicated? lol

1 Like

I think index is a fine way…you can use the index as the id as long as you don’t allow deletes.

2 Likes

It looks like I’m still in the right path and I’m relieve, thank you @skilesare.

1 Like