Skip to content

Conversation

@tompng
Copy link
Member

@tompng tompng commented Oct 17, 2025

Fixes #1126

In prompt calculation, main.to_s was called on every keystroke and every line in multiline input.
Cache prompt parts(%m and %M) so that main.to_s is only called once per read-eval cycle.

Also fixes this problem.

irb(main):001> def self.to_s = 'a' * rand(1..10)
=> :to_s
# before
irb(aaa):002* def f 
irb(aaaaaaa):003*   puts 'Hello'
irb(aa):004*   puts 'World'
irb(aaaa):005> end

# after
irb(aaaa):002* def f
irb(aaaa):003*   puts 'Hello'
irb(aaaa):004*   puts 'World'
irb(aaaa):005> end

@tompng tompng force-pushed the prompt_main_string_performance branch from cd56ed4 to fdea403 Compare October 18, 2025 11:01
In prompt calculation, `main.to_s` was called on every keystroke and every line in multiline input.
Cache prompt parts(%m, %M) so that `main.to_s` is only called once per read-eval cycle.
@tompng tompng force-pushed the prompt_main_string_performance branch from fdea403 to 35eb9e2 Compare October 18, 2025 11:05
end

def format_prompt(format, ltype, indent, line_no) # :nodoc:
part_cache = @prompt_part_cache || {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since format_prompt is only called in generate_prompt, is it possible to be called outside of readmultiline context? (perhaps in the dyamic_prompt callback?)

If it can be, let's also wrap the caller inside the wrapper mentioned above to provide caching?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Currently, generate_prompt without cache is only used in test code testing format_prompt.
I'd like to make cache mechanism optional because:

  • Testing format_prompt is easier.
  • Always requiring wrapper is simple, but also introduces some sort of (small) complexity in call chain.

(But I'm not really sure, maybe wrong)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Then let's at least add comments to explain why it could be nil, like

Suggested change
part_cache = @prompt_part_cache || {}
# @prompt_part_cache could be nil in unit tests
part_cache = @prompt_part_cache || {}

lib/irb.rb Outdated
end

def readmultiline
@prompt_part_cache = {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can create a wrapper to ensure wherever we can generate prompt we have this set and cleaned. Like

with_prompt_part_cached do
  ...
end

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice catch! updated

end

def format_prompt(format, ltype, indent, line_no) # :nodoc:
part_cache = @prompt_part_cache || {}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see. Then let's at least add comments to explain why it could be nil, like

Suggested change
part_cache = @prompt_part_cache || {}
# @prompt_part_cache could be nil in unit tests
part_cache = @prompt_part_cache || {}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: %m in default IRB prompt can make the console unusably slow for objects with huge to_s/inspect

2 participants