enableStringInterning analyzes columns and enables interning for low-cardinality string columns This can save 30-70% memory for datasets with repeated categorical values
()
| 692 | // enableStringInterning analyzes columns and enables interning for low-cardinality string columns |
| 693 | // This can save 30-70% memory for datasets with repeated categorical values |
| 694 | func (b *Buffer) enableStringInterning() { |
| 695 | b.mu.Lock() |
| 696 | defer b.mu.Unlock() |
| 697 | |
| 698 | if b.rowLen < 100 { |
| 699 | return // Too small to benefit |
| 700 | } |
| 701 | |
| 702 | // Initialize interning structures |
| 703 | b.interners = make([]*stringInterner, b.colLen) |
| 704 | b.internCols = make([]bool, b.colLen) |
| 705 | |
| 706 | // Analyze each column |
| 707 | for col := 0; col < b.colLen; col++ { |
| 708 | // Skip non-string columns |
| 709 | if b.colType[col] != colTypeStr { |
| 710 | continue |
| 711 | } |
| 712 | |
| 713 | // Get column data |
| 714 | colData := make([]string, b.rowLen) |
| 715 | for row := 0; row < b.rowLen; row++ { |
| 716 | if col < len(b.cont[row]) { |
| 717 | colData[row] = b.cont[row][col] |
| 718 | } |
| 719 | } |
| 720 | |
| 721 | // Check if column should be interned (low cardinality) |
| 722 | if shouldInternColumn(colData, internCardinalityThreshold) { |
| 723 | b.interners[col] = newStringInterner() |
| 724 | b.internCols[col] = true |
| 725 | |
| 726 | // Intern existing values |
| 727 | for row := 0; row < b.rowLen; row++ { |
| 728 | if col < len(b.cont[row]) { |
| 729 | b.cont[row][col] = b.interners[col].intern(b.cont[row][col]) |
| 730 | } |
| 731 | } |
| 732 | } |
| 733 | } |
| 734 | } |
| 735 | |
| 736 | // internValue interns a string value for a specific column if interning is enabled |
| 737 | func (b *Buffer) internValue(col int, value string) string { |